from datetime import datetime, timedelta from typing import Optional from fastapi import Depends, HTTPException, status from jose import jwt, JWTError from fastapi.security import OAuth2PasswordBearer import bcrypt from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select from app.models import User from app.schemas import Token from app.db import get_db import os # Загружаем переменные окружения SECRET_KEY = os.getenv("SECRET_KEY") ALGORITHM = os.getenv("ALGORITHM", "HS256") ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "60")) # --------------------------------------------------------- # Хэширование пароля # --------------------------------------------------------- def hash_password(password: str) -> str: pwd_bytes = password.encode('utf-8') salt = bcrypt.gensalt() return bcrypt.hashpw(pwd_bytes, salt).decode('utf-8') def verify_password(plain_password: str, hashed_password: str) -> bool: return bcrypt.checkpw( plain_password.encode('utf-8'), hashed_password.encode('utf-8') ) # --------------------------------------------------------- # Создание JWT-токена # --------------------------------------------------------- def create_access_token(data: dict, expires_delta: Optional[int] = None): to_encode = data.copy() expire = datetime.utcnow() + timedelta( minutes=expires_delta or ACCESS_TOKEN_EXPIRE_MINUTES ) to_encode.update({"exp": expire}) return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) # --------------------------------------------------------- # Получение текущего пользователя по токену # --------------------------------------------------------- oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") async def get_current_user( token: str = Depends(oauth2_scheme), db: AsyncSession = Depends(get_db) ): """ Достаём токен из Authorization Bearer """ if not token or not isinstance(token, str): raise HTTPException(status_code=401, detail="Missing token") try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) email: str = payload.get("sub") if email is None: raise HTTPException(status_code=401, detail="Invalid token payload") except JWTError: raise HTTPException(status_code=401, detail="Invalid or expired token") query = select(User).where(User.email == email) result = await db.execute(query) user = result.scalar_one_or_none() if user is None: raise HTTPException(status_code=404, detail="User not found") return user