Spaces:
Running
Running
| import os | |
| from typing import Optional | |
| import jwt | |
| from fastapi import Header, HTTPException, status | |
| JWT_SECRET = os.getenv("JWT_SECRET", "") | |
| JWT_ALGO = "HS256" | |
| def _decode(token: str) -> dict: | |
| return jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGO]) | |
| def _strip_bearer(authorization: Optional[str]) -> Optional[str]: | |
| if not authorization: | |
| return None | |
| parts = authorization.split(None, 1) | |
| if len(parts) == 2 and parts[0].lower() == "bearer": | |
| return parts[1].strip() | |
| # Some frontends send the raw token (admin panel sets a `token` header). | |
| return authorization.strip() | |
| def get_current_user_id( | |
| authorization: Optional[str] = Header(None), | |
| token: Optional[str] = Header(None), # admin frontend's `token` header | |
| ) -> int: | |
| raw = _strip_bearer(authorization) or (token.strip() if token else None) | |
| if not raw: | |
| raise HTTPException( | |
| status_code=status.HTTP_401_UNAUTHORIZED, | |
| detail="Missing Authorization token", | |
| ) | |
| try: | |
| payload = _decode(raw) | |
| except jwt.ExpiredSignatureError: | |
| raise HTTPException(401, "Token expired") | |
| except jwt.InvalidTokenError: | |
| raise HTTPException(401, "Invalid token") | |
| try: | |
| return int(payload["sub"]) | |
| except (KeyError, ValueError, TypeError): | |
| raise HTTPException(401, "Token has no user id") | |
| def get_optional_user_id( | |
| authorization: Optional[str] = Header(None), | |
| token: Optional[str] = Header(None), | |
| ) -> Optional[int]: | |
| """Like get_current_user_id, but returns None if no/invalid token.""" | |
| raw = _strip_bearer(authorization) or (token.strip() if token else None) | |
| if not raw: | |
| return None | |
| try: | |
| payload = _decode(raw) | |
| return int(payload["sub"]) | |
| except Exception: | |
| return None | |
| # Admin guard | |
| import asyncpg as _asyncpg | |
| from fastapi import Depends as _Depends | |
| from app.db.asyncpg_pool import get_asyncpg_conn as _get_conn | |
| async def get_current_admin( | |
| user_id: int = _Depends(get_current_user_id), | |
| conn: _asyncpg.Connection = _Depends(_get_conn), | |
| ) -> int: | |
| """ | |
| Validates the JWT, then checks the user's role in the DB. | |
| Returns user_id on success; raises 403 if the user isn't an admin. | |
| """ | |
| role = await conn.fetchval( | |
| "SELECT role FROM users WHERE id = $1", user_id | |
| ) | |
| if role != "admin": | |
| raise HTTPException( | |
| status_code=status.HTTP_403_FORBIDDEN, | |
| detail="Admin access required", | |
| ) | |
| return user_id | |