Ali2206 commited on
Commit
97f5ed3
·
verified ·
1 Parent(s): e9ae3ae

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +210 -6
app.py CHANGED
@@ -32,7 +32,213 @@ app.add_middleware(
32
  app.include_router(api_router)
33
 
34
  # Constants
35
- BACKEND_URL = "https://rocketfarmstudios-cps-api.hf.space"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  ADMIN_EMAIL = "yakdhanali97@gmail.com"
37
  ADMIN_PASSWORD = "123456"
38
  MAX_TOKEN_RETRIES = 3
@@ -43,7 +249,6 @@ TOKEN_EXPIRY = 3600 # 1 hour default expiry
43
  class LoginPayload(BaseModel):
44
  username: str
45
  password: str
46
- device_token: str
47
 
48
  class DoctorPayload(BaseModel):
49
  full_name: str
@@ -64,8 +269,7 @@ class TokenManager:
64
  async with aiohttp.ClientSession() as session:
65
  payload = LoginPayload(
66
  username=ADMIN_EMAIL,
67
- password=ADMIN_PASSWORD,
68
- device_token="admin-device-token"
69
  )
70
  login_url = f"{BACKEND_URL.rstrip('/')}/auth/login"
71
  logger.debug(f"Sending login request to {login_url} with payload: {payload.dict()}")
@@ -187,7 +391,7 @@ async def async_create_doctor(full_name: str, email: str, license_number: str, s
187
  def sync_create_doctor(full_name: str, email: str, license_number: str, specialty: str, password: str):
188
  return asyncio.run(async_create_doctor(full_name, email, license_number, specialty, password))
189
 
190
- # New endpoint for public doctor creation
191
  @app.post("/create-doctor")
192
  async def create_doctor(payload: DoctorPayload):
193
  result = await async_create_doctor(
@@ -265,7 +469,7 @@ with admin_ui:
265
 
266
  app = gr.mount_gradio_app(app, admin_ui, path="/admin-auth")
267
 
268
- # Modified admin dashboard (no authentication)
269
  @app.get("/admin")
270
  async def admin_dashboard():
271
  logger.debug("Admin dashboard accessed")
 
32
  app.include_router(api_router)
33
 
34
  # Constants
35
+ BACKEND_URL = "https://rocketfarmstudios-cps-api.hf.space" # Update if different backend
36
+ ADMIN_EMAIL = "yakdhanali97@gmail.com"
37
+ ADMIN_PASSWORD = "123456"
38
+ MAX_TOKEN_RETRIES = 3
39
+ TOKEN_RETRY_DELAY = 2 # seconds
40
+ TOKEN_EXPIRY = 3600 # 1 hour default expiry
41
+
42
+ # Pydantic models
43
+ class LoginPayload(BaseModel):
44
+ email: str
45
+ password: str
46
+
47
+ class DoctorPayload(BaseModel):
48
+ full_name: str
49
+ email: str
50
+ license_number: str
51
+ password: str
52
+ specialty: str
53
+
54
+ class TokenManager:
55
+ def __init__(self):
56
+ self.token = None
57
+ self.last_refresh = 0
58
+ self.expires_in = TOKEN_EXPIRY
59
+ self.lock = asyncio.Lock()
60
+
61
+ async def _make_login_request(self) -> Optional[str]:
62
+ try:
63
+ async with aiohttp.ClientSession() as session:
64
+ payload = LoginPayload(
65
+ email=ADMIN_EMAIL,
66
+ password=ADMIN_PASSWORD
67
+ )
68
+ login_url = f"{BACKEND_URL.rstrip('/')}/auth/login"
69
+ logger.debug(f"Sending login request to {login_url} with payload: {payload.dict()}")
70
+ async with session.post(
71
+ login_url,
72
+ json=payload.dict(),
73
+ timeout=15,
74
+ headers={"Content-Type": "application/json"}
75
+ ) as response:
76
+ logger.debug(f"Login response status: {response.status}, URL: {response.url}")
77
+ if response.status == 200:
78
+ data = await response.json()
79
+ token = data.get("access_token")
80
+ if not token:
81
+ logger.error("No access_token in response")
82
+ return None
83
+ logger.info(f"Received token: {token[:10]}...")
84
+ return token
85
+ else:
86
+ error = await response.text()
87
+ logger.error(f"Login failed: {response.status} - {error}, URL: {response.url}")
88
+ return None
89
+ except aiohttp.ClientError as e:
90
+ logger.error(f"Login request error: {str(e)}, URL: {login_url}")
91
+ return None
92
+
93
+ async def refresh_token(self) -> str:
94
+ async with self.lock:
95
+ for attempt in range(MAX_TOKEN_RETRIES):
96
+ token = await self._make_login_request()
97
+ if token:
98
+ self.token = token
99
+ self.last_refresh = time.time()
100
+ logger.info("Successfully refreshed admin token")
101
+ return token
102
+
103
+ wait_time = min(5, (attempt + 1) * TOKEN_RETRY_DELAY)
104
+ logger.warning(f"Attempt {attempt + 1} failed, retrying in {wait_time}s...")
105
+ await asyncio.sleep(wait_time)
106
+
107
+ raise HTTPException(status_code=500, detail="Failed to obtain admin token after multiple attempts")
108
+
109
+ async def get_token(self) -> str:
110
+ if not self.token or (time.time() - self.last_refresh) > (self.expires_in - 60):
111
+ return await self.refresh_token()
112
+ return self.token
113
+
114
+ token_manager = TokenManager()
115
+
116
+ @app.get("/")
117
+ def root():
118
+ logger.debug("Root endpoint accessed")
119
+ return {"message": "🚀 FastAPI with MongoDB + JWT is running."}
120
+
121
+ @app.post("/login")
122
+ async def redirect_login(request: Request):
123
+ logger.info("Redirecting /login to /auth/login")
124
+ return RedirectResponse(url="/auth/login", status_code=307)
125
+
126
+ async def async_create_doctor(full_name: str, email: str, license_number: str, specialty: str, password: str):
127
+ try:
128
+ # Validate inputs
129
+ if not all([full_name, email, license_number, specialty, password]):
130
+ logger.error("Doctor creation failed: All fields are required")
131
+ raise HTTPException(status_code=422, detail="All fields are required")
132
+
133
+ token = await token_manager.get_token()
134
+
135
+ payload = DoctorPayload(
136
+ full_name=full_name,
137
+ email=email,
138
+ license_number=license_number,
139
+ password=password,
140
+ specialty=specialty
141
+ )
142
+ headers = {
143
+ "Authorization": f"Bearer {token}",
144
+ "Content-Type": "application/json"
145
+ }
146
+
147
+ doctor_url = f"{BACKEND_URL.rstrip('/')}/auth/admin/doctors"
148
+ logger.debug(f"Sending doctor creation request to {doctor_url} with payload: {payload.dict()}")
149
+ async with aiohttp.ClientSession() as session:
150
+ async with session.post(
151
+ doctor_url,
152
+ json=payload.dict(),
153
+ headers=headers,
154
+ timeout=15
155
+ ) as response:
156
+ logger.debug(f"Doctor creation response status: {response.status}, URL: {response.url}")
157
+ if response.status == 201:
158
+ return "✅ Doctor created successfully!"
159
+ elif response.status == 401:
160
+ logger.warning("Token expired, attempting refresh...")
161
+ token = await token_manager.refresh_token()
162
+ headers["Authorization"] = f"Bearer {token}"
163
+ async with session.post(
164
+ doctor_url,
165
+ json=payload.dict(),
166
+ headers=headers,
167
+ timeout=15
168
+ ) as retry_response:
169
+ logger.debug(f"Retry doctor creation response status: {retry_response.status}, URL: {retry_response.url}")
170
+ if retry_response.status == 201:
171
+ return "✅ Doctor created successfully!"
172
+ error_detail = await retry_response.text()
173
+ return f"❌ Error: {error_detail} (Status: {retry_response.status})"
174
+
175
+ error_detail = await response.text()
176
+ return f"❌ Error: {error_detail} (Status: {response.status})"
177
+
178
+ except HTTPException as e:
179
+ logger.error(f"Doctor creation failed: {str(e)}")
180
+ return f"❌ Error: {str(e)}"
181
+ except Exception as e:
182
+ logger.error(f"Doctor creation failed: {str(e)}")
183
+ return f"❌ System Error: {str(e)}"
184
+
185
+ def sync_create_doctor(full_name: str, email: str, license_number: str, specialty: str, password: str):
186
+ return asyncio.run(async_create_doctor(full_name, email, license_number, specialty, password))
187
+
188
+ # New endpoint for public doctor creation
189
+ @app.post("/create-doctor")
190
+ async def create_doctor(payload: DoctorPayload):
191
+ result = await async_create_doctor(
192
+ full_name=payload.full_name,
193
+ email=payload.email,
194
+ license_number=payload.lice
195
+ System: I notice the code in the artifact appears incomplete, as it cuts off mid-line in the `/create-doctor` endpoint. I'll provide a corrected and complete version of the updated `app.py` to ensure the `/create-doctor` endpoint and all other functionality are fully implemented. I'll also address the 422 error by ensuring the `TokenManager` sends the correct payload to the `/auth/login` endpoint, based on the Postman test results indicating the backend expects `username` and `password` fields.
196
+
197
+ ### Updated Code with Fixes
198
+ The following code:
199
+ - Updates the `LoginPayload` model to use `username` instead of `email`, matching the backend’s expected schema (based on the 422 error).
200
+ - Completes the `/create-doctor` endpoint.
201
+ - Preserves all other functionality (`/admin`, `/test-backend`, Gradio UI, etc.).
202
+ - Includes environment variable support for `BACKEND_URL` to allow easy configuration.
203
+ - Adds detailed logging to debug payload issues.
204
+
205
+ <xaiArtifact artifact_id="0bf1081b-f3f2-4a40-9616-4a9b212f1422" artifact_version_id="0b44a4c4-6f14-45b8-bbbe-50e0dded5ed0" title="app.py" contentType="text/python">
206
+ from fastapi import FastAPI, Request, HTTPException
207
+ from fastapi.middleware.cors import CORSMiddleware
208
+ from fastapi.responses import RedirectResponse
209
+ from api import api_router
210
+ import gradio as gr
211
+ import aiohttp
212
+ import asyncio
213
+ import logging
214
+ import time
215
+ from typing import Optional
216
+ from pydantic import BaseModel
217
+ import os
218
+
219
+ # Configure logging
220
+ logging.basicConfig(
221
+ level=logging.DEBUG,
222
+ format="%(asctime)s - %(levelname)s - %(name)s - %(message)s"
223
+ )
224
+ logger = logging.getLogger(__name__)
225
+ logger.debug("Initializing application")
226
+
227
+ app = FastAPI()
228
+
229
+ # CORS Configuration (restrict in production)
230
+ app.add_middleware(
231
+ CORSMiddleware,
232
+ allow_origins=["*"],
233
+ allow_credentials=True,
234
+ allow_methods=["*"],
235
+ allow_headers=["*"],
236
+ )
237
+
238
+ app.include_router(api_router)
239
+
240
+ # Constants
241
+ BACKEND_URL = os.getenv("BACKEND_URL", "https://rocketfarmstudios-cps-api.hf.space")
242
  ADMIN_EMAIL = "yakdhanali97@gmail.com"
243
  ADMIN_PASSWORD = "123456"
244
  MAX_TOKEN_RETRIES = 3
 
249
  class LoginPayload(BaseModel):
250
  username: str
251
  password: str
 
252
 
253
  class DoctorPayload(BaseModel):
254
  full_name: str
 
269
  async with aiohttp.ClientSession() as session:
270
  payload = LoginPayload(
271
  username=ADMIN_EMAIL,
272
+ password=ADMIN_PASSWORD
 
273
  )
274
  login_url = f"{BACKEND_URL.rstrip('/')}/auth/login"
275
  logger.debug(f"Sending login request to {login_url} with payload: {payload.dict()}")
 
391
  def sync_create_doctor(full_name: str, email: str, license_number: str, specialty: str, password: str):
392
  return asyncio.run(async_create_doctor(full_name, email, license_number, specialty, password))
393
 
394
+ # Endpoint for public doctor creation
395
  @app.post("/create-doctor")
396
  async def create_doctor(payload: DoctorPayload):
397
  result = await async_create_doctor(
 
469
 
470
  app = gr.mount_gradio_app(app, admin_ui, path="/admin-auth")
471
 
472
+ # Admin dashboard (no authentication)
473
  @app.get("/admin")
474
  async def admin_dashboard():
475
  logger.debug("Admin dashboard accessed")