from fastapi import Depends, HTTPException, status from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer import jwt from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.config import get_settings from app.core.db import get_db from app.core.errors import api_error from app.models.models import User settings = get_settings() bearer = HTTPBearer(auto_error=False) async def get_current_user( credentials: HTTPAuthorizationCredentials | None = Depends(bearer), db: AsyncSession = Depends(get_db), ) -> User: if not credentials: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=api_error("missing_token", "Missing token"), ) token = credentials.credentials try: payload = jwt.decode(token, settings.jwt_secret_key, algorithms=[settings.jwt_algorithm]) except jwt.InvalidTokenError as exc: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=api_error("invalid_token", "Invalid token"), ) from exc if payload.get("type") != "access": raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=api_error("invalid_token_type", "Invalid token type"), ) user_id = payload.get("sub") user = await db.scalar(select(User).where(User.id == int(user_id))) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=api_error("user_not_found", "User not found"), ) return user def require_roles(*roles: str): async def role_dependency(user: User = Depends(get_current_user)) -> User: if user.role not in roles: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail=api_error("forbidden", "Forbidden"), ) return user return role_dependency