nex_basse/backend/app/core/deps.py

66 lines
2.4 KiB
Python

from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session
from redis import Redis
from app.core.db import get_db
from app.core.security import decode_token
from app.core.redis import get_redis
from app.models import User
from app.models.enums import StatusEnum
bearer_scheme = HTTPBearer()
async def get_current_user(
creds: HTTPAuthorizationCredentials = Depends(bearer_scheme),
db: Session = Depends(get_db),
redis: Redis = Depends(get_redis),
) -> User:
token = creds.credentials
try:
payload = decode_token(token)
except ValueError:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
if payload.get("type") != "access":
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
user_id = payload.get("sub")
# Enforce redis-backed online session; if missing, force re-login
online_key = f"auth:online:{user_id}"
is_online = await redis.exists(online_key)
if not is_online:
# Fallback: if refresh token exists for this user, recreate online key
cursor = 0
found_ttl = None
while True:
cursor, keys = await redis.scan(cursor=cursor, match="auth:refresh:*", count=200)
for key in keys:
try:
val = await redis.get(key)
if isinstance(val, bytes):
val = val.decode('utf-8')
if val == str(user_id):
ttl = await redis.ttl(key)
if ttl and ttl > 0:
found_ttl = ttl
break
except Exception:
continue
if found_ttl is not None or cursor == 0:
break
if found_ttl is not None:
await redis.setex(online_key, found_ttl, "1")
else:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Session expired")
user = db.query(User).filter(User.user_id == int(user_id), User.is_deleted == 0).first()
if not user or user.status != int(StatusEnum.ENABLED):
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User disabled")
return user