CPS-API / app.py
Ali2206's picture
Update app.py
360e197 verified
Raw
History Blame Contribute Delete
7.8 kB
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)