import uuid from datetime import datetime from fastapi import Depends, HTTPException, Request, status from fastapi.security import OAuth2PasswordBearer from slowapi import Limiter from slowapi.util import get_remote_address from sqlalchemy.orm import Session from core.auth import decode_access_token from core.database import get_db from core.models import User as UserModel oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login") # Shared rate limiter — registered on app.state in main.py limiter = Limiter(key_func=get_remote_address) def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)) -> UserModel: user_id = decode_access_token(token) if not user_id: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") user = db.query(UserModel).filter(UserModel.id == uuid.UUID(user_id)).first() if not user: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found") # Throttle to one write per 5 minutes so every authenticated request doesn't hammer the DB now = datetime.now() if not user.last_active_at or (now - user.last_active_at).total_seconds() > 300: user.last_active_at = now db.commit() return user # Per-user key for rate limiting authenticated endpoints — prevents shared IPs (NAT/VPN) # from having their limits pooled. Falls back to remote IP for unauthenticated requests. def get_user_id_from_request(request: Request) -> str: auth = request.headers.get("Authorization", "") if auth.startswith("Bearer "): user_id = decode_access_token(auth[7:]) if user_id: return f"user:{user_id}" return get_remote_address(request)