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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +115 -61
app.py CHANGED
@@ -1,23 +1,26 @@
1
- from fastapi import FastAPI, Request, HTTPException, Response
2
  from fastapi.middleware.cors import CORSMiddleware
3
- from fastapi.responses import RedirectResponse, HTMLResponse
4
  from api import api_router
5
  import gradio as gr
6
- import requests
7
- import logging
8
- import time
9
  import aiohttp
10
  import asyncio
 
 
11
  from typing import Optional
 
12
 
13
  # Configure logging
14
- logging.basicConfig(level=logging.DEBUG)
 
 
 
15
  logger = logging.getLogger(__name__)
16
  logger.debug("Initializing application")
17
 
18
  app = FastAPI()
19
 
20
- # CORS Configuration
21
  app.add_middleware(
22
  CORSMiddleware,
23
  allow_origins=["*"],
@@ -34,35 +37,59 @@ ADMIN_EMAIL = "yakdhanali97@gmail.com"
34
  ADMIN_PASSWORD = "123456"
35
  MAX_TOKEN_RETRIES = 3
36
  TOKEN_RETRY_DELAY = 2 # seconds
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
  class TokenManager:
39
  def __init__(self):
40
  self.token = None
41
  self.last_refresh = 0
42
- self.expires_in = 3600 # 1 hour default expiry
43
  self.lock = asyncio.Lock()
44
 
45
  async def _make_login_request(self) -> Optional[str]:
46
  try:
47
  async with aiohttp.ClientSession() as session:
 
 
 
 
 
 
 
48
  async with session.post(
49
- f"{BACKEND_URL}/auth/login",
50
- json={
51
- "username": ADMIN_EMAIL,
52
- "password": ADMIN_PASSWORD,
53
- "device_token": "admin-device-token"
54
- },
55
- timeout=10
56
  ) as response:
 
57
  if response.status == 200:
58
  data = await response.json()
59
- return data.get("access_token")
 
 
 
 
 
60
  else:
61
  error = await response.text()
62
- logger.error(f"Login failed: {response.status} - {error}")
63
  return None
64
- except Exception as e:
65
- logger.error(f"Login request error: {str(e)}")
66
  return None
67
 
68
  async def refresh_token(self) -> str:
@@ -75,11 +102,11 @@ class TokenManager:
75
  logger.info("Successfully refreshed admin token")
76
  return token
77
 
78
- wait_time = min(5, (attempt + 1) * 2) # Exponential backoff with max 5s
79
  logger.warning(f"Attempt {attempt + 1} failed, retrying in {wait_time}s...")
80
  await asyncio.sleep(wait_time)
81
 
82
- raise Exception("Failed to obtain admin token after multiple attempts")
83
 
84
  async def get_token(self) -> str:
85
  if not self.token or (time.time() - self.last_refresh) > (self.expires_in - 60):
@@ -98,61 +125,93 @@ async def redirect_login(request: Request):
98
  logger.info("Redirecting /login to /auth/login")
99
  return RedirectResponse(url="/auth/login", status_code=307)
100
 
101
- def authenticate_admin(email: str = None, password: str = None):
102
- if email != ADMIN_EMAIL or password != ADMIN_PASSWORD:
103
- logger.warning(f"Failed admin login attempt with email: {email}")
104
- raise HTTPException(status_code=401, detail="Unauthorized: Invalid email or password")
105
-
106
- logger.info(f"Admin authenticated successfully: {email}")
107
- return True
108
-
109
- async def async_create_doctor(full_name, email, matricule, password, specialty):
110
  try:
 
 
 
 
 
111
  token = await token_manager.get_token()
112
 
113
- payload = {
114
- "full_name": full_name,
115
- "email": email,
116
- "license_number": matricule,
117
- "password": password,
118
- "specialty": specialty,
119
- }
120
  headers = {
121
  "Authorization": f"Bearer {token}",
122
  "Content-Type": "application/json"
123
  }
124
 
 
 
125
  async with aiohttp.ClientSession() as session:
126
  async with session.post(
127
- f"{BACKEND_URL}/auth/admin/doctors",
128
- json=payload,
129
  headers=headers,
130
- timeout=10
131
  ) as response:
 
132
  if response.status == 201:
133
  return "✅ Doctor created successfully!"
134
- elif response.status == 401: # Token might be expired
135
  logger.warning("Token expired, attempting refresh...")
136
  token = await token_manager.refresh_token()
137
  headers["Authorization"] = f"Bearer {token}"
138
  async with session.post(
139
- f"{BACKEND_URL}/auth/admin/doctors",
140
- json=payload,
141
  headers=headers,
142
- timeout=10
143
  ) as retry_response:
 
144
  if retry_response.status == 201:
145
  return "✅ Doctor created successfully!"
 
 
146
 
147
  error_detail = await response.text()
148
  return f"❌ Error: {error_detail} (Status: {response.status})"
149
 
 
 
 
150
  except Exception as e:
151
  logger.error(f"Doctor creation failed: {str(e)}")
152
  return f"❌ System Error: {str(e)}"
153
 
154
- def sync_create_doctor(*args):
155
- return asyncio.run(async_create_doctor(*args))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  admin_ui = gr.Blocks(
158
  css="""
@@ -186,14 +245,15 @@ with admin_ui:
186
  gr.Markdown("# Doctor Account Creator")
187
 
188
  with gr.Column():
189
- full_name = gr.Textbox(label="Full Name")
190
- email = gr.Textbox(label="Email")
191
- matricule = gr.Textbox(label="License Number")
192
  specialty = gr.Dropdown(
193
  label="Specialty",
194
- choices=["General Practice", "Cardiology", "Neurology", "Pediatrics"]
 
195
  )
196
- password = gr.Textbox(label="Password", type="password")
197
  submit_btn = gr.Button("Create Account")
198
  output = gr.Textbox(label="Status", interactive=False)
199
 
@@ -205,24 +265,18 @@ with admin_ui:
205
 
206
  app = gr.mount_gradio_app(app, admin_ui, path="/admin-auth")
207
 
 
208
  @app.get("/admin")
209
- async def admin_dashboard(email: str = None, password: str = None, response: Response = None):
210
  logger.debug("Admin dashboard accessed")
211
- try:
212
- authenticate_admin(email, password)
213
- return RedirectResponse(url="/admin-auth", status_code=307)
214
- except HTTPException as e:
215
- response.status_code = 401
216
- return HTMLResponse(content="""
217
- <h1>401 Unauthorized</h1>
218
- <p>Invalid admin credentials</p>
219
- """)
220
 
221
  @app.on_event("startup")
222
  async def startup_event():
223
  """Initialize token but don't fail startup"""
224
  try:
225
  await token_manager.get_token()
 
226
  except Exception as e:
227
  logger.error(f"Initial token fetch failed: {str(e)}")
228
 
 
1
+ from fastapi import FastAPI, Request, HTTPException
2
  from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.responses import RedirectResponse
4
  from api import api_router
5
  import gradio as gr
 
 
 
6
  import aiohttp
7
  import asyncio
8
+ import logging
9
+ import time
10
  from typing import Optional
11
+ from pydantic import BaseModel
12
 
13
  # Configure logging
14
+ logging.basicConfig(
15
+ level=logging.DEBUG,
16
+ format="%(asctime)s - %(levelname)s - %(name)s - %(message)s"
17
+ )
18
  logger = logging.getLogger(__name__)
19
  logger.debug("Initializing application")
20
 
21
  app = FastAPI()
22
 
23
+ # CORS Configuration (restrict in production)
24
  app.add_middleware(
25
  CORSMiddleware,
26
  allow_origins=["*"],
 
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
+ username: str
45
+ password: str
46
+ device_token: str
47
+
48
+ class DoctorPayload(BaseModel):
49
+ full_name: str
50
+ email: str
51
+ license_number: str
52
+ password: str
53
+ specialty: str
54
 
55
  class TokenManager:
56
  def __init__(self):
57
  self.token = None
58
  self.last_refresh = 0
59
+ self.expires_in = TOKEN_EXPIRY
60
  self.lock = asyncio.Lock()
61
 
62
  async def _make_login_request(self) -> Optional[str]:
63
  try:
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()}")
72
  async with session.post(
73
+ login_url,
74
+ json=payload.dict(),
75
+ timeout=15,
76
+ headers={"Content-Type": "application/json"}
 
 
 
77
  ) as response:
78
+ logger.debug(f"Login response status: {response.status}, URL: {response.url}")
79
  if response.status == 200:
80
  data = await response.json()
81
+ token = data.get("access_token")
82
+ if not token:
83
+ logger.error("No access_token in response")
84
+ return None
85
+ logger.info(f"Received token: {token[:10]}...")
86
+ return token
87
  else:
88
  error = await response.text()
89
+ logger.error(f"Login failed: {response.status} - {error}, URL: {response.url}")
90
  return None
91
+ except aiohttp.ClientError as e:
92
+ logger.error(f"Login request error: {str(e)}, URL: {login_url}")
93
  return None
94
 
95
  async def refresh_token(self) -> str:
 
102
  logger.info("Successfully refreshed admin token")
103
  return token
104
 
105
+ wait_time = min(5, (attempt + 1) * TOKEN_RETRY_DELAY)
106
  logger.warning(f"Attempt {attempt + 1} failed, retrying in {wait_time}s...")
107
  await asyncio.sleep(wait_time)
108
 
109
+ raise HTTPException(status_code=500, detail="Failed to obtain admin token after multiple attempts")
110
 
111
  async def get_token(self) -> str:
112
  if not self.token or (time.time() - self.last_refresh) > (self.expires_in - 60):
 
125
  logger.info("Redirecting /login to /auth/login")
126
  return RedirectResponse(url="/auth/login", status_code=307)
127
 
128
+ async def async_create_doctor(full_name: str, email: str, license_number: str, specialty: str, password: str):
 
 
 
 
 
 
 
 
129
  try:
130
+ # Validate inputs
131
+ if not all([full_name, email, license_number, specialty, password]):
132
+ logger.error("Doctor creation failed: All fields are required")
133
+ raise HTTPException(status_code=422, detail="All fields are required")
134
+
135
  token = await token_manager.get_token()
136
 
137
+ payload = DoctorPayload(
138
+ full_name=full_name,
139
+ email=email,
140
+ license_number=license_number,
141
+ password=password,
142
+ specialty=specialty
143
+ )
144
  headers = {
145
  "Authorization": f"Bearer {token}",
146
  "Content-Type": "application/json"
147
  }
148
 
149
+ doctor_url = f"{BACKEND_URL.rstrip('/')}/auth/admin/doctors"
150
+ logger.debug(f"Sending doctor creation request to {doctor_url} with payload: {payload.dict()}")
151
  async with aiohttp.ClientSession() as session:
152
  async with session.post(
153
+ doctor_url,
154
+ json=payload.dict(),
155
  headers=headers,
156
+ timeout=15
157
  ) as response:
158
+ logger.debug(f"Doctor creation response status: {response.status}, URL: {response.url}")
159
  if response.status == 201:
160
  return "✅ Doctor created successfully!"
161
+ elif response.status == 401:
162
  logger.warning("Token expired, attempting refresh...")
163
  token = await token_manager.refresh_token()
164
  headers["Authorization"] = f"Bearer {token}"
165
  async with session.post(
166
+ doctor_url,
167
+ json=payload.dict(),
168
  headers=headers,
169
+ timeout=15
170
  ) as retry_response:
171
+ logger.debug(f"Retry doctor creation response status: {retry_response.status}, URL: {retry_response.url}")
172
  if retry_response.status == 201:
173
  return "✅ Doctor created successfully!"
174
+ error_detail = await retry_response.text()
175
+ return f"❌ Error: {error_detail} (Status: {retry_response.status})"
176
 
177
  error_detail = await response.text()
178
  return f"❌ Error: {error_detail} (Status: {response.status})"
179
 
180
+ except HTTPException as e:
181
+ logger.error(f"Doctor creation failed: {str(e)}")
182
+ return f"❌ Error: {str(e)}"
183
  except Exception as e:
184
  logger.error(f"Doctor creation failed: {str(e)}")
185
  return f"❌ System Error: {str(e)}"
186
 
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(
194
+ full_name=payload.full_name,
195
+ email=payload.email,
196
+ license_number=payload.license_number,
197
+ specialty=payload.specialty,
198
+ password=payload.password
199
+ )
200
+ return {"result": result}
201
+
202
+ # Test endpoint to verify backend connectivity
203
+ @app.get("/test-backend")
204
+ async def test_backend():
205
+ try:
206
+ async with aiohttp.ClientSession() as session:
207
+ test_url = f"{BACKEND_URL.rstrip('/')}/"
208
+ logger.debug(f"Testing backend connectivity to {test_url}")
209
+ async with session.get(test_url, timeout=5) as response:
210
+ logger.debug(f"Test backend response status: {response.status}, URL: {response.url}")
211
+ return {"status": response.status, "url": str(response.url), "message": await response.text()}
212
+ except aiohttp.ClientError as e:
213
+ logger.error(f"Test backend error: {str(e)}")
214
+ return {"status": "error", "message": str(e)}
215
 
216
  admin_ui = gr.Blocks(
217
  css="""
 
245
  gr.Markdown("# Doctor Account Creator")
246
 
247
  with gr.Column():
248
+ full_name = gr.Textbox(label="Full Name", placeholder="e.g., Dr. John Doe")
249
+ email = gr.Textbox(label="Email", placeholder="e.g., john.doe@example.com")
250
+ matricule = gr.Textbox(label="License Number", placeholder="e.g., 12345")
251
  specialty = gr.Dropdown(
252
  label="Specialty",
253
+ choices=["General Practice", "Cardiology", "Neurology", "Pediatrics"],
254
+ value="General Practice"
255
  )
256
+ password = gr.Textbox(label="Password", type="password", placeholder="Enter a secure password")
257
  submit_btn = gr.Button("Create Account")
258
  output = gr.Textbox(label="Status", interactive=False)
259
 
 
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")
272
+ return RedirectResponse(url="/admin-auth", status_code=307)
 
 
 
 
 
 
 
 
273
 
274
  @app.on_event("startup")
275
  async def startup_event():
276
  """Initialize token but don't fail startup"""
277
  try:
278
  await token_manager.get_token()
279
+ logger.info("Initial token fetch successful")
280
  except Exception as e:
281
  logger.error(f"Initial token fetch failed: {str(e)}")
282