from datetime import datetime, timedelta from typing import Optional from jose import JWTError, jwt from passlib.context import CryptContext from fastapi import Depends, HTTPException, status, Request from fastapi.security import OAuth2PasswordBearer from sqlalchemy.orm import Session from database import get_db, User import os SECRET_KEY = os.getenv("SECRET_KEY", "your-secret-key-change-this-in-production") ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 7 # 7 дней pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login", auto_error=False) def decode_token(token: str) -> Optional[dict]: """Декодирует JWT токен""" if not token: # <-- Добавлена проверка return None try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) return payload except JWTError: return None def verify_password(plain_password: str, hashed_password: str) -> bool: return pwd_context.verify(plain_password, hashed_password) def get_password_hash(password: str) -> str: return pwd_context.hash(password) def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str: to_encode = data.copy() if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt async def get_current_user_from_token(token: str, db: Session) -> Optional[User]: """Получает пользователя из JWT токена""" if not token: # <-- Добавлена проверка return None payload = decode_token(token) if not payload: return None username = payload.get("sub") if not username: return None user = db.query(User).filter(User.username == username).first() return user async def get_current_user_optional(request: Request, db: Session = Depends(get_db)) -> Optional[User]: token = request.cookies.get("access_token") if not token: # <-- Исправлено: сразу возвращаем None return None if token.startswith("Bearer "): token = token[7:] return await get_current_user_from_token(token, db) async def get_current_user(request: Request, db: Session = Depends(get_db)) -> User: token = request.cookies.get("access_token") if not token: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated", headers={"WWW-Authenticate": "Bearer"}, ) if token.startswith("Bearer "): token = token[7:] user = await get_current_user_from_token(token, db) if user is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid authentication credentials", headers={"WWW-Authenticate": "Bearer"}, ) return user def create_user(db: Session, username: str, password: str) -> User: hashed_password = get_password_hash(password) user = User(username=username, hashed_password=hashed_password, balance=1000.0) db.add(user) db.commit() db.refresh(user) return user def authenticate_user(db: Session, username: str, password: str) -> Optional[User]: user = db.query(User).filter(User.username == username).first() if not user: return None if not verify_password(password, user.hashed_password): return None return user def is_item_craftable(item_id: int) -> bool: """Проверяет, можно ли использовать предмет в контракте""" from backend import get_item, is_final_in_collection item = get_item(item_id) if not item: return False if not item.get("is_craftable", True): return False if is_final_in_collection(item): return False return True async def get_current_admin(request: Request, db: Session = Depends(get_db)) -> User: """Проверяет, что пользователь - админ""" user = await get_current_user(request, db) if not user.is_admin: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Admin access required" ) return user