from passlib.context import CryptContext from jose import JWTError, jwt from datetime import datetime, timedelta from typing import Optional from fastapi import Depends, HTTPException, status, Cookie from sqlalchemy.orm import Session from models import get_db, User SECRET_KEY = "speechify-jwt-secret-2025-change-in-prod" ALGORITHM = "HS256" TOKEN_EXPIRE_HOURS = 48 pwd = CryptContext(schemes=["bcrypt"], deprecated="auto") def hash_password(password: str) -> str: return pwd.hash(password) def verify_password(plain: str, hashed: str) -> bool: return pwd.verify(plain, hashed) def create_token(user_id: int, username: str, role: str) -> str: expire = datetime.utcnow() + timedelta(hours=TOKEN_EXPIRE_HOURS) return jwt.encode( {"sub": str(user_id), "username": username, "role": role, "exp": expire}, SECRET_KEY, algorithm=ALGORITHM, ) def decode_token(token: str) -> Optional[dict]: try: return jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) except JWTError: return None def get_current_user( token: Optional[str] = Cookie(None, alias="auth_token"), db: Session = Depends(get_db), ) -> User: if not token: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated") payload = decode_token(token) if not payload: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") user = db.query(User).filter(User.id == int(payload["sub"])).first() if not user or not user.is_active: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found") return user def require_admin(user: User = Depends(get_current_user)) -> User: if user.role != "admin": raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Admin required") return user