CPS-API / app.py
Ali2206's picture
Update app.py
360e197 verified
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()
@app.get("/")
def root():
logger.debug("Root endpoint accessed")
return {"message": "πŸš€ FastAPI with MongoDB + JWT is running."}
@app.post("/login")
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")
@app.get("/admin")
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>
""")
@app.on_event("startup")
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)