Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI, Request, HTTPException, Response | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import RedirectResponse, HTMLResponse | |
| from api import api_router | |
| import gradio as gr | |
| import requests | |
| import logging | |
| import time | |
| import aiohttp | |
| import asyncio | |
| from typing import Optional | |
| # Configure logging | |
| logging.basicConfig(level=logging.DEBUG) | |
| logger = logging.getLogger(__name__) | |
| logger.debug("Initializing application") | |
| app = FastAPI() | |
| # CORS Configuration | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| app.include_router(api_router) | |
| # Constants | |
| BACKEND_URL = "https://rocketfarmstudios-cps-api.hf.space" | |
| ADMIN_EMAIL = "yakdhanali97@gmail.com" | |
| ADMIN_PASSWORD = "123456" | |
| MAX_TOKEN_RETRIES = 3 | |
| TOKEN_RETRY_DELAY = 2 # seconds | |
| class TokenManager: | |
| def __init__(self): | |
| self.token = None | |
| self.last_refresh = 0 | |
| self.expires_in = 3600 # 1 hour default expiry | |
| self.lock = asyncio.Lock() | |
| async def _make_login_request(self) -> Optional[str]: | |
| try: | |
| async with aiohttp.ClientSession() as session: | |
| async with session.post( | |
| f"{BACKEND_URL}/auth/login", | |
| json={ | |
| "username": ADMIN_EMAIL, | |
| "password": ADMIN_PASSWORD, | |
| "device_token": "admin-device-token" | |
| }, | |
| timeout=10 | |
| ) as response: | |
| if response.status == 200: | |
| data = await response.json() | |
| return data.get("access_token") | |
| else: | |
| error = await response.text() | |
| logger.error(f"Login failed: {response.status} - {error}") | |
| return None | |
| except Exception as e: | |
| logger.error(f"Login request error: {str(e)}") | |
| return None | |
| async def refresh_token(self) -> str: | |
| async with self.lock: | |
| for attempt in range(MAX_TOKEN_RETRIES): | |
| token = await self._make_login_request() | |
| if token: | |
| self.token = token | |
| self.last_refresh = time.time() | |
| logger.info("Successfully refreshed admin token") | |
| return token | |
| wait_time = min(5, (attempt + 1) * 2) # Exponential backoff with max 5s | |
| logger.warning(f"Attempt {attempt + 1} failed, retrying in {wait_time}s...") | |
| await asyncio.sleep(wait_time) | |
| raise Exception("Failed to obtain admin token after multiple attempts") | |
| async def get_token(self) -> str: | |
| if not self.token or (time.time() - self.last_refresh) > (self.expires_in - 60): | |
| return await self.refresh_token() | |
| return self.token | |
| token_manager = TokenManager() | |
| def root(): | |
| logger.debug("Root endpoint accessed") | |
| return {"message": "π FastAPI with MongoDB + JWT is running."} | |
| async def redirect_login(request: Request): | |
| logger.info("Redirecting /login to /auth/login") | |
| return RedirectResponse(url="/auth/login", status_code=307) | |
| def authenticate_admin(email: str = None, password: str = None): | |
| if email != ADMIN_EMAIL or password != ADMIN_PASSWORD: | |
| logger.warning(f"Failed admin login attempt with email: {email}") | |
| raise HTTPException(status_code=401, detail="Unauthorized: Invalid email or password") | |
| logger.info(f"Admin authenticated successfully: {email}") | |
| return True | |
| async def async_create_doctor(full_name, email, matricule, password, specialty): | |
| try: | |
| token = await token_manager.get_token() | |
| payload = { | |
| "full_name": full_name, | |
| "email": email, | |
| "license_number": matricule, | |
| "password": password, | |
| "specialty": specialty, | |
| } | |
| headers = { | |
| "Authorization": f"Bearer {token}", | |
| "Content-Type": "application/json" | |
| } | |
| async with aiohttp.ClientSession() as session: | |
| async with session.post( | |
| f"{BACKEND_URL}/auth/admin/doctors", | |
| json=payload, | |
| headers=headers, | |
| timeout=10 | |
| ) as response: | |
| if response.status == 201: | |
| return "β Doctor created successfully!" | |
| elif response.status == 401: # Token might be expired | |
| logger.warning("Token expired, attempting refresh...") | |
| token = await token_manager.refresh_token() | |
| headers["Authorization"] = f"Bearer {token}" | |
| async with session.post( | |
| f"{BACKEND_URL}/auth/admin/doctors", | |
| json=payload, | |
| headers=headers, | |
| timeout=10 | |
| ) as retry_response: | |
| if retry_response.status == 201: | |
| return "β Doctor created successfully!" | |
| error_detail = await response.text() | |
| return f"β Error: {error_detail} (Status: {response.status})" | |
| except Exception as e: | |
| logger.error(f"Doctor creation failed: {str(e)}") | |
| return f"β System Error: {str(e)}" | |
| def sync_create_doctor(*args): | |
| return asyncio.run(async_create_doctor(*args)) | |
| admin_ui = gr.Blocks( | |
| css=""" | |
| .gradio-container { | |
| font-family: Arial, sans-serif; | |
| max-width: 800px; | |
| margin: 0 auto; | |
| padding: 2rem; | |
| } | |
| .input-group { | |
| margin-bottom: 1.5rem; | |
| } | |
| input, select { | |
| width: 100%; | |
| padding: 0.5rem; | |
| margin-bottom: 1rem; | |
| } | |
| button { | |
| background-color: #4a6fa5; | |
| color: white; | |
| padding: 0.75rem; | |
| border: none; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| width: 100%; | |
| } | |
| """ | |
| ) | |
| with admin_ui: | |
| gr.Markdown("# Doctor Account Creator") | |
| with gr.Column(): | |
| full_name = gr.Textbox(label="Full Name") | |
| email = gr.Textbox(label="Email") | |
| matricule = gr.Textbox(label="License Number") | |
| specialty = gr.Dropdown( | |
| label="Specialty", | |
| choices=["General Practice", "Cardiology", "Neurology", "Pediatrics"] | |
| ) | |
| password = gr.Textbox(label="Password", type="password") | |
| submit_btn = gr.Button("Create Account") | |
| output = gr.Textbox(label="Status", interactive=False) | |
| submit_btn.click( | |
| fn=sync_create_doctor, | |
| inputs=[full_name, email, matricule, specialty, password], | |
| outputs=output | |
| ) | |
| app = gr.mount_gradio_app(app, admin_ui, path="/admin-auth") | |
| async def admin_dashboard(email: str = None, password: str = None, response: Response = None): | |
| logger.debug("Admin dashboard accessed") | |
| try: | |
| authenticate_admin(email, password) | |
| return RedirectResponse(url="/admin-auth", status_code=307) | |
| except HTTPException as e: | |
| response.status_code = 401 | |
| return HTMLResponse(content=""" | |
| <h1>401 Unauthorized</h1> | |
| <p>Invalid admin credentials</p> | |
| """) | |
| async def startup_event(): | |
| """Initialize token but don't fail startup""" | |
| try: | |
| await token_manager.get_token() | |
| except Exception as e: | |
| logger.error(f"Initial token fetch failed: {str(e)}") | |
| if __name__ == "__main__": | |
| logger.info("Starting application") | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=7860) |