| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576 |
- from datetime import datetime, timedelta, timezone
- from typing import Any, Optional
- from jose import jwt, JWTError
- from passlib.context import CryptContext
- from fastapi import Depends, HTTPException, status
- from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
- from redis.asyncio import Redis
- from app.core.config import get_settings
- settings = get_settings()
- pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
- security_scheme = HTTPBearer()
- def verify_password(plain_password: str, hashed_password: str) -> bool:
- return pwd_context.verify(plain_password, hashed_password)
- def hash_password(password: str) -> str:
- return pwd_context.hash(password)
- def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
- to_encode = data.copy()
- expire = datetime.now(timezone.utc) + (
- expires_delta or timedelta(hours=24)
- )
- to_encode.update({"exp": expire, "iat": datetime.now(timezone.utc)})
- return jwt.encode(to_encode, settings.secret_key, algorithm="HS256")
- def decode_access_token(token: str) -> Optional[dict]:
- try:
- return jwt.decode(token, settings.secret_key, algorithms=["HS256"])
- except (JWTError, Exception):
- return None
- async def get_current_user(
- credentials: HTTPAuthorizationCredentials = Depends(security_scheme),
- ) -> dict:
- payload = decode_access_token(credentials.credentials)
- if payload is None:
- raise HTTPException(
- status_code=status.HTTP_401_UNAUTHORIZED,
- detail="Invalid or expired token",
- )
- return payload
- class RateLimiter:
- def __init__(self, redis_client: Redis):
- self.redis = redis_client
- async def is_rate_limited(self, user_id: str) -> bool:
- minute_key = f"rate_limit:minute:{user_id}"
- hour_key = f"rate_limit:hour:{user_id}"
- current_minute = await self.redis.get(minute_key)
- current_hour = await self.redis.get(hour_key)
- if current_minute and int(current_minute) >= settings.rate_limit_per_minute:
- return True
- if current_hour and int(current_hour) >= settings.rate_limit_per_hour:
- return True
- async with self.redis.pipeline() as pipe:
- pipe.incr(minute_key)
- pipe.expire(minute_key, 60)
- pipe.incr(hour_key)
- pipe.expire(hour_key, 3600)
- await pipe.execute()
- return False
|