Ali2206 commited on
Commit
360e197
·
verified ·
1 Parent(s): 28e2912

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +90 -103
app.py CHANGED
@@ -1,31 +1,23 @@
1
-
2
- from fastapi import FastAPI, Request, HTTPException
3
  from fastapi.middleware.cors import CORSMiddleware
4
- from fastapi.responses import RedirectResponse
5
  from api import api_router
6
  import gradio as gr
7
- import aiohttp
8
- import asyncio
9
  import logging
10
  import time
 
 
11
  from typing import Optional
12
- from pydantic import BaseModel
13
- import os
14
- from db.mongo import users_collection
15
- from core.security import hash_password
16
- from datetime import datetime
17
 
18
  # Configure logging
19
- logging.basicConfig(
20
- level=logging.DEBUG,
21
- format="%(asctime)s - %(levelname)s - %(name)s - %(message)s"
22
- )
23
  logger = logging.getLogger(__name__)
24
  logger.debug("Initializing application")
25
 
26
  app = FastAPI()
27
 
28
- # CORS Configuration (restrict in production)
29
  app.add_middleware(
30
  CORSMiddleware,
31
  allow_origins=["*"],
@@ -37,62 +29,40 @@ app.add_middleware(
37
  app.include_router(api_router)
38
 
39
  # Constants
40
- BACKEND_URL = os.getenv("BACKEND_URL", "https://rocketfarmstudios-cps-api.hf.space")
41
  ADMIN_EMAIL = "yakdhanali97@gmail.com"
42
  ADMIN_PASSWORD = "123456"
43
  MAX_TOKEN_RETRIES = 3
44
  TOKEN_RETRY_DELAY = 2 # seconds
45
- TOKEN_EXPIRY = 3600 # 1 hour default expiry
46
-
47
- # Pydantic models
48
- class LoginPayload(BaseModel):
49
- username: str
50
- password: str
51
-
52
- class DoctorPayload(BaseModel):
53
- full_name: str
54
- email: str
55
- license_number: str
56
- password: str
57
- specialty: str
58
 
59
  class TokenManager:
60
  def __init__(self):
61
  self.token = None
62
  self.last_refresh = 0
63
- self.expires_in = TOKEN_EXPIRY
64
  self.lock = asyncio.Lock()
65
 
66
  async def _make_login_request(self) -> Optional[str]:
67
  try:
68
  async with aiohttp.ClientSession() as session:
69
- payload = LoginPayload(
70
- username=ADMIN_EMAIL,
71
- password=ADMIN_PASSWORD
72
- )
73
- login_url = f"{BACKEND_URL.rstrip('/')}/auth/login"
74
- logger.debug(f"Sending login request to {login_url} with payload: {payload.dict()}")
75
  async with session.post(
76
- login_url,
77
- json=payload.dict(),
78
- timeout=15,
79
- headers={"Content-Type": "application/json"}
 
 
 
80
  ) as response:
81
- logger.debug(f"Login response status: {response.status}, URL: {response.url}")
82
  if response.status == 200:
83
  data = await response.json()
84
- token = data.get("access_token")
85
- if not token:
86
- logger.error("No access_token in response")
87
- return None
88
- logger.info(f"Received token: {token[:10]}...")
89
- return token
90
  else:
91
  error = await response.text()
92
- logger.error(f"Login failed: {response.status} - {error}, URL: {response.url}")
93
  return None
94
- except aiohttp.ClientError as e:
95
- logger.error(f"Login request error: {str(e)}, URL: {login_url}")
96
  return None
97
 
98
  async def refresh_token(self) -> str:
@@ -104,12 +74,12 @@ class TokenManager:
104
  self.last_refresh = time.time()
105
  logger.info("Successfully refreshed admin token")
106
  return token
107
-
108
- wait_time = min(5, (attempt + 1) * TOKEN_RETRY_DELAY)
109
  logger.warning(f"Attempt {attempt + 1} failed, retrying in {wait_time}s...")
110
  await asyncio.sleep(wait_time)
111
 
112
- raise HTTPException(status_code=500, detail="Failed to obtain admin token after multiple attempts")
113
 
114
  async def get_token(self) -> str:
115
  if not self.token or (time.time() - self.last_refresh) > (self.expires_in - 60):
@@ -121,56 +91,68 @@ token_manager = TokenManager()
121
  @app.get("/")
122
  def root():
123
  logger.debug("Root endpoint accessed")
124
- return {"message": "\ud83d\ude80 FastAPI with MongoDB + JWT is running."}
125
 
126
  @app.post("/login")
127
  async def redirect_login(request: Request):
128
  logger.info("Redirecting /login to /auth/login")
129
  return RedirectResponse(url="/auth/login", status_code=307)
130
 
131
- # NEW PUBLIC DOCTOR CREATION ENDPOINT (NO AUTH REQUIRED)
132
- @app.post("/public/create-doctor")
133
- async def create_doctor_public(payload: DoctorPayload):
 
 
 
 
 
 
134
  try:
135
- logger.info(f"[PUBLIC] Creating doctor: {payload.email}")
136
- email = payload.email.strip().lower()
137
- existing = await users_collection.find_one({"email": email})
138
- if existing:
139
- raise HTTPException(status_code=409, detail="Email already exists")
140
-
141
- hashed_pw = hash_password(payload.password)
142
- doctor_doc = {
143
  "email": email,
144
- "full_name": payload.full_name.strip(),
145
- "password": hashed_pw,
146
- "role": "doctor",
147
- "specialty": payload.specialty,
148
- "license_number": payload.license_number,
149
- "created_at": datetime.utcnow().isoformat(),
150
- "updated_at": datetime.utcnow().isoformat(),
151
- "device_token": ""
152
  }
153
- result = await users_collection.insert_one(doctor_doc)
154
- logger.info(f"[PUBLIC] Doctor inserted successfully: {email}")
155
- return {"status": "success", "id": str(result.inserted_id), "email": email}
156
-
157
- except HTTPException:
158
- raise
159
- except Exception as e:
160
- logger.error(f"[PUBLIC] Failed to create doctor: {str(e)}")
161
- raise HTTPException(status_code=500, detail="Failed to create doctor")point to verify backend connectivity
162
- @app.get("/test-backend")
163
- async def test_backend():
164
- try:
165
  async with aiohttp.ClientSession() as session:
166
- test_url = f"{BACKEND_URL.rstrip('/')}/"
167
- logger.debug(f"Testing backend connectivity to {test_url}")
168
- async with session.get(test_url, timeout=5) as response:
169
- logger.debug(f"Test backend response status: {response.status}, URL: {response.url}")
170
- return {"status": response.status, "url": str(response.url), "message": await response.text()}
171
- except aiohttp.ClientError as e:
172
- logger.error(f"Test backend error: {str(e)}")
173
- return {"status": "error", "message": str(e)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
  admin_ui = gr.Blocks(
176
  css="""
@@ -204,15 +186,14 @@ with admin_ui:
204
  gr.Markdown("# Doctor Account Creator")
205
 
206
  with gr.Column():
207
- full_name = gr.Textbox(label="Full Name", placeholder="e.g., Dr. John Doe")
208
- email = gr.Textbox(label="Email", placeholder="e.g., john.doe@example.com")
209
- matricule = gr.Textbox(label="License Number", placeholder="e.g., 12345")
210
  specialty = gr.Dropdown(
211
  label="Specialty",
212
- choices=["General Practice", "Cardiology", "Neurology", "Pediatrics"],
213
- value="General Practice"
214
  )
215
- password = gr.Textbox(label="Password", type="password", placeholder="Enter a secure password")
216
  submit_btn = gr.Button("Create Account")
217
  output = gr.Textbox(label="Status", interactive=False)
218
 
@@ -224,18 +205,24 @@ with admin_ui:
224
 
225
  app = gr.mount_gradio_app(app, admin_ui, path="/admin-auth")
226
 
227
- # Admin dashboard (no authentication)
228
  @app.get("/admin")
229
- async def admin_dashboard():
230
  logger.debug("Admin dashboard accessed")
231
- return RedirectResponse(url="/admin-auth", status_code=307)
 
 
 
 
 
 
 
 
232
 
233
  @app.on_event("startup")
234
  async def startup_event():
235
  """Initialize token but don't fail startup"""
236
  try:
237
  await token_manager.get_token()
238
- logger.info("Initial token fetch successful")
239
  except Exception as e:
240
  logger.error(f"Initial token fetch failed: {str(e)}")
241
 
 
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=["*"],
 
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:
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:
 
74
  self.last_refresh = time.time()
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):
 
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="""
 
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
 
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