Ali2206 commited on
Commit
23f156e
·
verified ·
1 Parent(s): 257cf3e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +141 -163
app.py CHANGED
@@ -1,44 +1,46 @@
1
- import os
2
- from fastapi import FastAPI, HTTPException, Request, Response
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from fastapi.responses import RedirectResponse, HTMLResponse
 
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, Dict, Any
12
- from passlib.context import CryptContext
13
- from passlib.exc import UnknownHashError
14
- from pydantic import BaseModel
15
 
16
  # Configure logging
17
- logging.basicConfig(
18
- level=logging.INFO,
19
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
20
- )
21
  logger = logging.getLogger(__name__)
 
 
 
22
 
23
- # Password hashing
24
- pwd_context = CryptContext(
25
- schemes=["bcrypt"],
26
- deprecated="auto"
 
 
 
27
  )
28
 
29
- # Configuration
30
- BACKEND_URL = os.getenv("BACKEND_URL", "https://rocketfarmstudios-cps-api.hf.space")
31
- ADMIN_EMAIL = os.getenv("ADMIN_EMAIL", "yakdhanali97@gmail.com")
32
- ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD", "123456")
 
 
 
 
33
 
34
  class TokenManager:
35
  def __init__(self):
36
  self.token = None
37
  self.last_refresh = 0
38
- self.expires_in = 3600
39
  self.lock = asyncio.Lock()
40
- self.initialized = False
41
- self.available = False
42
 
43
  async def _make_login_request(self) -> Optional[str]:
44
  try:
@@ -48,13 +50,12 @@ class TokenManager:
48
  json={
49
  "username": ADMIN_EMAIL,
50
  "password": ADMIN_PASSWORD,
51
- "device_token": "admin-console"
52
  },
53
- timeout=aiohttp.ClientTimeout(total=10)
54
  ) as response:
55
  if response.status == 200:
56
  data = await response.json()
57
- self.available = True
58
  return data.get("access_token")
59
  else:
60
  error = await response.text()
@@ -64,9 +65,9 @@ class TokenManager:
64
  logger.error(f"Login request error: {str(e)}")
65
  return None
66
 
67
- async def refresh_token(self) -> Optional[str]:
68
  async with self.lock:
69
- for attempt in range(3):
70
  token = await self._make_login_request()
71
  if token:
72
  self.token = token
@@ -74,181 +75,158 @@ class TokenManager:
74
  logger.info("Successfully refreshed admin token")
75
  return token
76
 
77
- wait_time = min(5, (attempt + 1) * 2)
78
  logger.warning(f"Attempt {attempt + 1} failed, retrying in {wait_time}s...")
79
  await asyncio.sleep(wait_time)
80
 
81
- self.available = False
82
- return None
83
 
84
- async def get_token(self) -> Optional[str]:
85
- if not self.initialized or not self.token or (time.time() - self.last_refresh) > (self.expires_in - 60):
86
- self.initialized = True
87
  return await self.refresh_token()
88
  return self.token
89
 
90
  token_manager = TokenManager()
91
 
92
- app = FastAPI(title="Medical Admin Portal")
93
-
94
- app.add_middleware(
95
- CORSMiddleware,
96
- allow_origins=["*"],
97
- allow_credentials=True,
98
- allow_methods=["*"],
99
- allow_headers=["*"],
100
- )
101
-
102
  @app.get("/")
103
- async def root():
104
- return {
105
- "status": "running",
106
- "admin_api_available": token_manager.available
107
- }
108
 
109
  @app.post("/login")
110
- async def handle_login():
 
111
  return RedirectResponse(url="/auth/login", status_code=307)
112
 
113
- @app.get("/health")
114
- async def health_check():
115
- return {
116
- "status": "healthy",
117
- "admin_api": "available" if token_manager.available else "unavailable"
118
- }
 
119
 
120
- async def create_doctor_api(doctor_data: Dict[str, Any]) -> Dict[str, Any]:
121
- if not token_manager.available:
122
- return {
123
- "success": False,
124
- "error": "Admin API currently unavailable"
 
 
 
 
 
125
  }
126
-
127
- token = await token_manager.get_token()
128
- if not token:
129
- return {
130
- "success": False,
131
- "error": "Failed to obtain admin token"
132
  }
133
-
134
- headers = {
135
- "Authorization": f"Bearer {token}",
136
- "Content-Type": "application/json"
137
- }
138
-
139
- try:
140
  async with aiohttp.ClientSession() as session:
141
  async with session.post(
142
  f"{BACKEND_URL}/auth/admin/doctors",
143
- json=doctor_data,
144
  headers=headers,
145
- timeout=aiohttp.ClientTimeout(total=15)
146
  ) as response:
147
  if response.status == 201:
148
- return {"success": True, "message": "Doctor created successfully"}
 
 
 
 
 
 
 
 
 
 
 
 
149
 
150
  error_detail = await response.text()
151
- return {
152
- "success": False,
153
- "error": f"Error {response.status}: {error_detail}"
154
- }
155
- except Exception as e:
156
- logger.error(f"Doctor creation API error: {str(e)}")
157
- return {
158
- "success": False,
159
- "error": f"API communication error: {str(e)}"
160
- }
161
-
162
- def sync_create_doctor(full_name: str, email: str, matricule: str, password: str, specialty: str) -> str:
163
- try:
164
- doctor_data = {
165
- "full_name": full_name,
166
- "email": email,
167
- "license_number": matricule,
168
- "password": password,
169
- "specialty": specialty
170
- }
171
-
172
- result = asyncio.run(create_doctor_api(doctor_data))
173
-
174
- if result["success"]:
175
- return "✅ " + result["message"]
176
- return "❌ " + result.get("error", "Unknown error")
177
-
178
  except Exception as e:
179
  logger.error(f"Doctor creation failed: {str(e)}")
180
- return f"❌ System error: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
 
182
- with gr.Blocks(title="Doctor Management") as admin_ui:
183
- gr.Markdown("# 👨‍⚕️ Doctor Account Creator")
184
-
185
- with gr.Row():
186
- with gr.Column():
187
- full_name = gr.Textbox(label="Full Name")
188
- email = gr.Textbox(label="Email")
189
- license_num = gr.Textbox(label="License Number")
190
-
191
- with gr.Column():
192
- specialty = gr.Dropdown(
193
- label="Specialty",
194
- choices=[
195
- "General Practice", "Cardiology", "Neurology",
196
- "Pediatrics", "Orthopedics", "Dermatology"
197
- ]
198
- )
199
- password = gr.Textbox(label="Password", type="password")
200
-
201
- submit_btn = gr.Button("Create Account")
202
- output = gr.Textbox(label="Status", interactive=False)
203
 
204
- submit_btn.click(
205
- fn=sync_create_doctor,
206
- inputs=[full_name, email, license_num, specialty, password],
207
- outputs=output
208
- )
209
-
210
- app = gr.mount_gradio_app(app, admin_ui, path="/admin")
211
-
212
- @app.get("/admin-auth")
213
- async def admin_auth(email: str, password: str, response: Response):
214
- if (email != ADMIN_EMAIL or
215
- not verify_password(password, pwd_context.hash(ADMIN_PASSWORD))):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
  response.status_code = 401
217
- return HTMLResponse("""
218
- <h1>Access Denied</h1>
219
  <p>Invalid admin credentials</p>
220
  """)
221
- return RedirectResponse(url="/admin")
222
 
223
  @app.on_event("startup")
224
  async def startup_event():
225
- """Try to connect to backend but don't fail startup"""
226
  try:
227
- logger.info("Testing backend connection...")
228
  await token_manager.get_token()
229
- if token_manager.available:
230
- logger.info("Backend connection successful")
231
- else:
232
- logger.warning("Backend connection failed - running in degraded mode")
233
- except Exception as e:
234
- logger.error(f"Startup connection test error: {str(e)}")
235
- token_manager.available = False
236
-
237
- def verify_password(plain_password: str, hashed_password: str) -> bool:
238
- try:
239
- return pwd_context.verify(plain_password, hashed_password)
240
- except UnknownHashError:
241
- logger.error(f"Unrecognized hash format")
242
- return False
243
  except Exception as e:
244
- logger.error(f"Password verification error: {str(e)}")
245
- return False
246
 
247
  if __name__ == "__main__":
 
248
  import uvicorn
249
- uvicorn.run(
250
- app,
251
- host="0.0.0.0",
252
- port=7860,
253
- log_level="info"
254
- )
 
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=["*"],
24
+ allow_credentials=True,
25
+ allow_methods=["*"],
26
+ allow_headers=["*"],
27
  )
28
 
29
+ app.include_router(api_router)
30
+
31
+ # Constants
32
+ BACKEND_URL = "https://rocketfarmstudios-cps-api.hf.space"
33
+ 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:
 
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()
 
65
  logger.error(f"Login request error: {str(e)}")
66
  return None
67
 
68
+ async def refresh_token(self) -> str:
69
  async with self.lock:
70
+ for attempt in range(MAX_TOKEN_RETRIES):
71
  token = await self._make_login_request()
72
  if token:
73
  self.token = token
 
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):
 
86
  return await self.refresh_token()
87
  return self.token
88
 
89
  token_manager = TokenManager()
90
 
 
 
 
 
 
 
 
 
 
 
91
  @app.get("/")
92
+ def root():
93
+ logger.debug("Root endpoint accessed")
94
+ return {"message": "🚀 FastAPI with MongoDB + JWT is running."}
 
 
95
 
96
  @app.post("/login")
97
+ 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="""
159
+ .gradio-container {
160
+ font-family: Arial, sans-serif;
161
+ max-width: 800px;
162
+ margin: 0 auto;
163
+ padding: 2rem;
164
+ }
165
+ .input-group {
166
+ margin-bottom: 1.5rem;
167
+ }
168
+ input, select {
169
+ width: 100%;
170
+ padding: 0.5rem;
171
+ margin-bottom: 1rem;
172
+ }
173
+ button {
174
+ background-color: #4a6fa5;
175
+ color: white;
176
+ padding: 0.75rem;
177
+ border: none;
178
+ border-radius: 4px;
179
+ cursor: pointer;
180
+ width: 100%;
181
+ }
182
+ """
183
+ )
184
 
185
+ 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
+
200
+ submit_btn.click(
201
+ fn=sync_create_doctor,
202
+ inputs=[full_name, email, matricule, specialty, password],
203
+ outputs=output
204
+ )
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
 
229
  if __name__ == "__main__":
230
+ logger.info("Starting application")
231
  import uvicorn
232
+ uvicorn.run(app, host="0.0.0.0", port=7860)