Spaces:
Sleeping
Sleeping
Nagesh Muralidhar
Simplify login, signup, and token handlers by directly accessing database and handling authentication
b7038e5
| import os | |
| import sys | |
| import shutil | |
| import logging | |
| import re | |
| from pathlib import Path | |
| # Set up logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # Add the backend directory to the Python path | |
| sys.path.append(os.path.abspath("backend")) | |
| # Create necessary directories if they don't exist | |
| os.makedirs("./temp_audio", exist_ok=True) | |
| os.makedirs("./temp", exist_ok=True) | |
| os.makedirs("./static", exist_ok=True) | |
| # Function to fix localhost URLs in JavaScript files | |
| def fix_js_files(): | |
| """Fix all JS files in the static directory to replace localhost:8000 with relative URLs""" | |
| assets_dir = Path("./static/assets") | |
| if not assets_dir.exists(): | |
| logger.warning(f"Assets directory not found at {assets_dir}") | |
| return | |
| logger.info(f"Searching for JS files in {assets_dir}") | |
| js_files = list(assets_dir.glob("*.js")) | |
| logger.info(f"Found {len(js_files)} JS files") | |
| for js_file in js_files: | |
| try: | |
| logger.info(f"Processing {js_file}") | |
| with open(js_file, "r", encoding="utf-8") as f: | |
| content = f.read() | |
| # Count occurrences before replacement | |
| count_before = content.count("localhost:8000") | |
| if count_before > 0: | |
| logger.info(f"Found {count_before} instances of localhost:8000 in {js_file}") | |
| # Replace localhost URLs with relative ones | |
| modified_content = content.replace("http://localhost:8000", "") | |
| modified_content = modified_content.replace("https://localhost:8000", "") | |
| # Create a backup just in case | |
| backup_file = js_file.with_suffix(".js.bak") | |
| shutil.copy(js_file, backup_file) | |
| # Write the modified content back | |
| with open(js_file, "w", encoding="utf-8") as f: | |
| f.write(modified_content) | |
| logger.info(f"Fixed {count_before} localhost URLs in {js_file}") | |
| except Exception as e: | |
| logger.error(f"Error processing {js_file}: {str(e)}") | |
| # Run the JS file fixer at startup | |
| fix_js_files() | |
| # Check for index.html and create a simple one if it doesn't exist | |
| static_path = Path("./static") | |
| index_path = static_path / "index.html" | |
| if not index_path.exists(): | |
| logger.warning("index.html not found in static directory, creating a simple one") | |
| with open(index_path, "w") as f: | |
| f.write("""<!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>PodCraft API</title> | |
| <style> | |
| body { font-family: Arial, sans-serif; margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f8f9fa; } | |
| .container { max-width: 800px; padding: 2rem; background-color: white; border-radius: 0.5rem; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } | |
| h1 { color: #6366F1; } | |
| pre { background-color: #f5f5f5; padding: 1rem; border-radius: 0.25rem; overflow: auto; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>PodCraft API</h1> | |
| <p>The PodCraft API is running. You can access the API at <a href="/docs">/docs</a>.</p> | |
| <p>API Status: <a href="/api/status">/api/status</a></p> | |
| </div> | |
| </body> | |
| </html>""") | |
| # Set environment variables for MongoDB connection timeout | |
| os.environ["MONGODB_CONNECT_TIMEOUT_MS"] = "5000" # 5 seconds timeout | |
| os.environ["MONGODB_SERVER_SELECTION_TIMEOUT_MS"] = "5000" # 5 seconds timeout | |
| # Import FastAPI and related modules | |
| from fastapi import FastAPI, Request, HTTPException, Depends | |
| from fastapi.responses import HTMLResponse, JSONResponse, FileResponse, Response, RedirectResponse | |
| from fastapi.staticfiles import StaticFiles | |
| from fastapi.middleware.cors import CORSMiddleware | |
| # Function to read index.html content | |
| def get_index_html(): | |
| if index_path.exists(): | |
| with open(index_path, "r") as f: | |
| return f.read() | |
| else: | |
| return "<html><body><h1>Index file not found</h1></body></html>" | |
| try: | |
| # Try to import the backend app but don't use it directly | |
| import backend.app.main | |
| logger.info("Backend module imported successfully") | |
| backend_available = True | |
| except Exception as e: | |
| logger.error(f"Error importing backend module: {str(e)}") | |
| backend_available = False | |
| # Create the main application | |
| app = FastAPI(title="PodCraft") | |
| # Add CORS middleware | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Define the root route that directly serves index.html | |
| async def root(): | |
| logger.info("Root route accessed, serving index.html") | |
| html_content = get_index_html() | |
| # Inject our URL rewriter scripts | |
| script_tag = ''' | |
| <script> | |
| (function() { | |
| console.log("Advanced URL Fixer running..."); | |
| // Intercept all network requests | |
| const originalFetch = window.fetch; | |
| window.fetch = function(url, options) { | |
| if (typeof url === 'string') { | |
| // Rewrite localhost:8000 | |
| if (url.includes('localhost:8000')) { | |
| url = url.replace('http://localhost:8000', ''); | |
| url = url.replace('https://localhost:8000', ''); | |
| console.log(`Rewriting URL from localhost:8000 to ${url}`); | |
| } | |
| // Handle relative URLs for API endpoints | |
| if (url.startsWith('/')) { | |
| if (!url.startsWith('/static/') && !url.startsWith('/assets/')) { | |
| console.log(`Using relative URL: ${url}`); | |
| } | |
| } | |
| } | |
| return originalFetch(url, options); | |
| }; | |
| // Override XMLHttpRequest | |
| const originalOpen = XMLHttpRequest.prototype.open; | |
| XMLHttpRequest.prototype.open = function(method, url, ...args) { | |
| if (typeof url === 'string') { | |
| // Rewrite localhost:8000 | |
| if (url.includes('localhost:8000')) { | |
| url = url.replace('http://localhost:8000', ''); | |
| url = url.replace('https://localhost:8000', ''); | |
| console.log(`Rewriting XHR URL from localhost:8000 to ${url}`); | |
| } | |
| } | |
| return originalOpen.call(this, method, url, ...args); | |
| }; | |
| // Attempt to hook into axios if it's being used | |
| if (window.axios) { | |
| const originalAxiosGet = window.axios.get; | |
| window.axios.get = function(url, config) { | |
| if (typeof url === 'string' && url.includes('localhost:8000')) { | |
| url = url.replace('http://localhost:8000', ''); | |
| console.log(`Rewriting axios URL from localhost:8000 to ${url}`); | |
| } | |
| return originalAxiosGet(url, config); | |
| }; | |
| const originalAxiosPost = window.axios.post; | |
| window.axios.post = function(url, data, config) { | |
| if (typeof url === 'string' && url.includes('localhost:8000')) { | |
| url = url.replace('http://localhost:8000', ''); | |
| console.log(`Rewriting axios POST URL from localhost:8000 to ${url}`); | |
| } | |
| return originalAxiosPost(url, data, config); | |
| }; | |
| } | |
| console.log("Advanced URL Fixer installed"); | |
| })(); | |
| </script> | |
| ''' | |
| if '</body>' in html_content: | |
| html_content = html_content.replace('</body>', f'{script_tag}</body>') | |
| else: | |
| html_content = html_content + script_tag | |
| return HTMLResponse(content=html_content) | |
| # API status endpoint | |
| async def status(): | |
| return {"status": "ok", "message": "PodCraft API is running"} | |
| # Serve static assets directory with correct content types | |
| async def serve_assets(file_path: str): | |
| file_full_path = static_path / "assets" / file_path | |
| if not file_full_path.exists() or not file_full_path.is_file(): | |
| logger.warning(f"Asset file not found: {file_full_path}") | |
| raise HTTPException(status_code=404, detail="Asset file not found") | |
| # Determine content type based on file extension | |
| content_type = None | |
| if file_path.endswith(".js"): | |
| content_type = "application/javascript" | |
| elif file_path.endswith(".css"): | |
| content_type = "text/css" | |
| elif file_path.endswith(".svg"): | |
| content_type = "image/svg+xml" | |
| elif file_path.endswith(".png"): | |
| content_type = "image/png" | |
| elif file_path.endswith(".jpg") or file_path.endswith(".jpeg"): | |
| content_type = "image/jpeg" | |
| logger.info(f"Serving asset: {file_path} with content-type: {content_type}") | |
| return FileResponse(file_full_path, media_type=content_type) | |
| # Special handlers for API routes that the React app calls directly | |
| async def login_proxy(request: Request): | |
| logger.info("Received login request at /login endpoint") | |
| if backend_available: | |
| try: | |
| # Get the JSON data from the request | |
| user_data = await request.json() | |
| logger.info(f"Login request data: {user_data}") | |
| # Extract username and password | |
| username = user_data.get("username") | |
| password = user_data.get("password") | |
| if not username or not password: | |
| logger.error("Missing username or password in login request") | |
| return JSONResponse( | |
| content={"error": "Missing username or password"}, | |
| status_code=400 | |
| ) | |
| # Import database connection and utility functions | |
| try: | |
| from backend.app.main import users, verify_password, create_access_token | |
| from datetime import timedelta | |
| except ImportError as e: | |
| logger.error(f"Error importing backend modules: {str(e)}") | |
| return JSONResponse( | |
| content={"error": "Backend configuration error"}, | |
| status_code=500 | |
| ) | |
| # Try to find the user in the database | |
| try: | |
| user = await users.find_one({"username": username}) | |
| if not user: | |
| logger.warning(f"User not found: {username}") | |
| return JSONResponse( | |
| content={"error": "Invalid username or password"}, | |
| status_code=401 | |
| ) | |
| # Verify password | |
| if not verify_password(password, user["password"]): | |
| logger.warning(f"Invalid password for user: {username}") | |
| return JSONResponse( | |
| content={"error": "Invalid username or password"}, | |
| status_code=401 | |
| ) | |
| # Create access token | |
| access_token = create_access_token( | |
| data={"sub": username} | |
| ) | |
| return JSONResponse( | |
| content={"access_token": access_token, "token_type": "bearer"}, | |
| status_code=200 | |
| ) | |
| except Exception as db_error: | |
| logger.error(f"Database error in login: {str(db_error)}") | |
| return JSONResponse( | |
| content={"error": "Database error", "message": str(db_error)}, | |
| status_code=500 | |
| ) | |
| except Exception as e: | |
| logger.error(f"General error in login_proxy: {str(e)}") | |
| return JSONResponse( | |
| content={"error": "Login failed", "message": str(e)}, | |
| status_code=500 | |
| ) | |
| else: | |
| logger.error("Backend not available for login") | |
| return JSONResponse( | |
| content={"error": "Backend API not available for login"}, | |
| status_code=503 | |
| ) | |
| async def signup_proxy(request: Request): | |
| logger.info("Received signup request at /signup endpoint") | |
| if backend_available: | |
| try: | |
| # Get the JSON data from the request | |
| user_data = await request.json() | |
| logger.info(f"Signup request data: {user_data}") | |
| # Extract username and password | |
| username = user_data.get("username") | |
| password = user_data.get("password") | |
| if not username or not password: | |
| logger.error("Missing username or password in signup request") | |
| return JSONResponse( | |
| content={"error": "Missing username or password"}, | |
| status_code=400 | |
| ) | |
| # Import database connection and utility functions | |
| try: | |
| from backend.app.main import users, get_password_hash | |
| except ImportError as e: | |
| logger.error(f"Error importing backend modules: {str(e)}") | |
| return JSONResponse( | |
| content={"error": "Backend configuration error"}, | |
| status_code=500 | |
| ) | |
| # Check if username exists | |
| try: | |
| existing_user = await users.find_one({"username": username}) | |
| if existing_user: | |
| logger.warning(f"Username already exists: {username}") | |
| return JSONResponse( | |
| content={"error": "Username already exists"}, | |
| status_code=400 | |
| ) | |
| # Hash the password and create user | |
| hashed_password = get_password_hash(password) | |
| # Insert the new user | |
| new_user = { | |
| "username": username, | |
| "password": hashed_password | |
| } | |
| await users.insert_one(new_user) | |
| return JSONResponse( | |
| content={"message": "User created successfully"}, | |
| status_code=201 | |
| ) | |
| except Exception as db_error: | |
| logger.error(f"Database error in signup: {str(db_error)}") | |
| return JSONResponse( | |
| content={"error": "Database error", "message": str(db_error)}, | |
| status_code=500 | |
| ) | |
| except Exception as e: | |
| logger.error(f"General error in signup_proxy: {str(e)}") | |
| return JSONResponse( | |
| content={"error": "Signup failed", "message": str(e)}, | |
| status_code=500 | |
| ) | |
| else: | |
| logger.error("Backend not available for signup") | |
| return JSONResponse( | |
| content={"error": "Backend API not available for signup"}, | |
| status_code=503 | |
| ) | |
| async def token_proxy(request: Request): | |
| logger.info("Received token request at /token endpoint") | |
| if backend_available: | |
| try: | |
| # Get form data from the request | |
| try: | |
| form_data = await request.form() | |
| username = form_data.get("username") | |
| password = form_data.get("password") | |
| if not username or not password: | |
| logger.error("Missing username or password in token request") | |
| return JSONResponse( | |
| content={"error": "Missing username or password"}, | |
| status_code=400 | |
| ) | |
| # Import database connection and utility functions | |
| try: | |
| from backend.app.main import users, verify_password, create_access_token | |
| from datetime import timedelta | |
| except ImportError as e: | |
| logger.error(f"Error importing backend modules: {str(e)}") | |
| return JSONResponse( | |
| content={"error": "Backend configuration error"}, | |
| status_code=500 | |
| ) | |
| # Try to find the user in the database | |
| try: | |
| user = await users.find_one({"username": username}) | |
| if not user: | |
| logger.warning(f"User not found: {username}") | |
| return JSONResponse( | |
| content={"error": "Invalid username or password"}, | |
| status_code=401 | |
| ) | |
| # Verify password | |
| if not verify_password(password, user["password"]): | |
| logger.warning(f"Invalid password for user: {username}") | |
| return JSONResponse( | |
| content={"error": "Invalid username or password"}, | |
| status_code=401 | |
| ) | |
| # Create access token | |
| access_token = create_access_token( | |
| data={"sub": username} | |
| ) | |
| return JSONResponse( | |
| content={"access_token": access_token, "token_type": "bearer"}, | |
| status_code=200 | |
| ) | |
| except Exception as db_error: | |
| logger.error(f"Database error in token request: {str(db_error)}") | |
| return JSONResponse( | |
| content={"error": "Database error", "message": str(db_error)}, | |
| status_code=500 | |
| ) | |
| except Exception as form_error: | |
| logger.error(f"Error processing form data in token request: {str(form_error)}") | |
| return JSONResponse( | |
| content={"error": "Invalid form data", "message": str(form_error)}, | |
| status_code=400 | |
| ) | |
| except Exception as e: | |
| logger.error(f"General error in token_proxy: {str(e)}") | |
| return JSONResponse( | |
| content={"error": "Token request failed", "message": str(e)}, | |
| status_code=500 | |
| ) | |
| else: | |
| logger.error("Backend not available for token request") | |
| return JSONResponse( | |
| content={"error": "Backend API not available for token request"}, | |
| status_code=503 | |
| ) | |
| # Mount the static directory for other static files | |
| if static_path.exists(): | |
| app.mount("/static", StaticFiles(directory=str(static_path)), name="static") | |
| # Catch-all route for client-side routing in the React app | |
| async def catch_all(full_path: str, request: Request): | |
| # Skip API paths and redirect them to the backend | |
| if full_path.startswith("api/") or full_path == "docs" or full_path == "openapi.json": | |
| if backend_available: | |
| # Use the backend.app.main module's routes | |
| return backend.app.main.app.get(full_path) | |
| else: | |
| return {"error": "Backend API not available"} | |
| # Skip static file paths | |
| if full_path.startswith("static/") or full_path.startswith("assets/"): | |
| raise HTTPException(status_code=404, detail="Not found") | |
| logger.info(f"Catch-all route hit for path: {full_path}, serving index.html for client-side routing") | |
| return HTMLResponse(content=get_index_html()) | |
| # For Hugging Face Spaces - expose the app | |
| if __name__ == "__main__": | |
| import uvicorn | |
| port = int(os.environ.get("PORT", 7860)) | |
| host = os.environ.get("HOST", "0.0.0.0") | |
| logger.info(f"Starting server on {host}:{port}") | |
| uvicorn.run(app, host=host, port=port) |