Spaces:
Sleeping
Sleeping
Hussien Haider commited on
Commit ·
77e48ab
1
Parent(s): f792754
- app/api/profile.py +63 -0
app/api/profile.py
CHANGED
|
@@ -21,6 +21,10 @@ class AvatarUpdateResponse(BaseModel):
|
|
| 21 |
avatar_url: str
|
| 22 |
|
| 23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
@router.get("", response_model=AvatarResponse)
|
| 25 |
async def get_profile(
|
| 26 |
user: dict = Depends(get_current_user),
|
|
@@ -48,6 +52,29 @@ async def get_profile(
|
|
| 48 |
)
|
| 49 |
|
| 50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
@router.post("/avatar", response_model=AvatarUpdateResponse)
|
| 52 |
async def upload_avatar(
|
| 53 |
file: UploadFile = File(...),
|
|
@@ -125,3 +152,39 @@ async def upload_avatar(
|
|
| 125 |
pass # Non-critical; the file is already uploaded
|
| 126 |
|
| 127 |
return AvatarUpdateResponse(avatar_url=public_url)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
avatar_url: str
|
| 22 |
|
| 23 |
|
| 24 |
+
class AvatarUrlPayload(BaseModel):
|
| 25 |
+
url: str
|
| 26 |
+
|
| 27 |
+
|
| 28 |
@router.get("", response_model=AvatarResponse)
|
| 29 |
async def get_profile(
|
| 30 |
user: dict = Depends(get_current_user),
|
|
|
|
| 52 |
)
|
| 53 |
|
| 54 |
|
| 55 |
+
@router.post("/avatar-url", response_model=AvatarUpdateResponse)
|
| 56 |
+
async def set_avatar_url(
|
| 57 |
+
payload: AvatarUrlPayload,
|
| 58 |
+
user: dict = Depends(get_current_user),
|
| 59 |
+
request: Request = None,
|
| 60 |
+
):
|
| 61 |
+
"""Save an external avatar URL (e.g. from Google OAuth) to the user's profile."""
|
| 62 |
+
user_id = user["user_id"]
|
| 63 |
+
user_token = extract_token_from_request(request) or SUPABASE_ANON_KEY
|
| 64 |
+
update_url = f"{SUPABASE_URL}/rest/v1/users?id=eq.{user_id}"
|
| 65 |
+
async with httpx.AsyncClient(timeout=15) as client:
|
| 66 |
+
await client.patch(
|
| 67 |
+
update_url,
|
| 68 |
+
json={"avatar_url": payload.url},
|
| 69 |
+
headers={
|
| 70 |
+
"apikey": SUPABASE_ANON_KEY,
|
| 71 |
+
"Authorization": f"Bearer {user_token}",
|
| 72 |
+
"Content-Type": "application/json",
|
| 73 |
+
},
|
| 74 |
+
)
|
| 75 |
+
return AvatarUpdateResponse(avatar_url=payload.url)
|
| 76 |
+
|
| 77 |
+
|
| 78 |
@router.post("/avatar", response_model=AvatarUpdateResponse)
|
| 79 |
async def upload_avatar(
|
| 80 |
file: UploadFile = File(...),
|
|
|
|
| 152 |
pass # Non-critical; the file is already uploaded
|
| 153 |
|
| 154 |
return AvatarUpdateResponse(avatar_url=public_url)
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
@router.get("/public/{user_id}")
|
| 158 |
+
async def get_public_profile(user_id: str):
|
| 159 |
+
"""Fetch a public user profile (no auth required)."""
|
| 160 |
+
headers = {
|
| 161 |
+
"apikey": SUPABASE_ANON_KEY,
|
| 162 |
+
"Authorization": f"Bearer {SUPABASE_ANON_KEY}",
|
| 163 |
+
"Content-Type": "application/json",
|
| 164 |
+
}
|
| 165 |
+
async with httpx.AsyncClient(timeout=15) as client:
|
| 166 |
+
url = f"{SUPABASE_URL}/rest/v1/leaderboard?id=eq.{user_id}&select=id,name,xp,completed_trainings,avatar_url&limit=1"
|
| 167 |
+
resp = await client.get(url, headers=headers)
|
| 168 |
+
user_data = {}
|
| 169 |
+
if resp.status_code == 200 and resp.json():
|
| 170 |
+
user_data = resp.json()[0]
|
| 171 |
+
else:
|
| 172 |
+
url2 = f"{SUPABASE_URL}/rest/v1/users?id=eq.{user_id}&select=id,name,avatar_url&limit=1"
|
| 173 |
+
resp2 = await client.get(url2, headers=headers)
|
| 174 |
+
if resp2.status_code == 200 and resp2.json():
|
| 175 |
+
u = resp2.json()[0]
|
| 176 |
+
user_data = {**u, "xp": 0, "completed_trainings": 0}
|
| 177 |
+
cert_url = f"{SUPABASE_URL}/rest/v1/certificates?user_id=eq.{user_id}&select=id,category,issue_date,verify_code,title&order=issue_date.desc"
|
| 178 |
+
cert_resp = await client.get(cert_url, headers=headers)
|
| 179 |
+
certificates = cert_resp.json() if cert_resp.status_code == 200 else []
|
| 180 |
+
return {
|
| 181 |
+
"user": {
|
| 182 |
+
"id": user_data.get("id", user_id),
|
| 183 |
+
"name": user_data.get("name") or "مستخدم",
|
| 184 |
+
"xp": int(user_data.get("xp") or 0),
|
| 185 |
+
"completed_trainings": int(user_data.get("completed_trainings") or 0),
|
| 186 |
+
"avatar_url": user_data.get("avatar_url") or "",
|
| 187 |
+
},
|
| 188 |
+
"certificates": certificates,
|
| 189 |
+
"total_certificates": len(certificates),
|
| 190 |
+
}
|