"""Reusable FastAPI dependencies.""" from __future__ import annotations import logging from typing import Annotated from fastapi import Depends, Header, HTTPException, status from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from jose import JWTError from sqlalchemy.engine import Connection from core.auth.utils import decode_access_token from database import get_db logger = logging.getLogger(__name__) _security = HTTPBearer() def get_current_user( credentials: Annotated[HTTPAuthorizationCredentials, Depends(_security)], db: Annotated[Connection, Depends(get_db)], ) -> dict: """Decode JWT and return user dict. Raises 401 on any failure.""" token = credentials.credentials try: payload = decode_access_token(token) except JWTError as e: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail={"code": "UNAUTHORIZED", "message": "Invalid or expired token"}, ) # Optionally verify user still exists in DB from core.dal.base_repository import BaseRepository from sqlalchemy import text row = db.execute( text("SELECT id, username, role FROM users WHERE id = :id"), {"id": int(payload["sub"])}, ).fetchone() if row is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail={"code": "UNAUTHORIZED", "message": "User not found"}, ) return dict(row._mapping) def require_admin( user: Annotated[dict, Depends(get_current_user)], ) -> dict: """Raise 403 if user is not admin.""" if user["role"] != "admin": raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail={"code": "FORBIDDEN", "message": "Admin role required"}, ) return user def get_server_or_404(server_id: int, db: Connection) -> dict: """Load server by ID or raise 404.""" from sqlalchemy import text row = db.execute( text("SELECT * FROM servers WHERE id = :id"), {"id": server_id} ).fetchone() if row is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail={"code": "NOT_FOUND", "message": f"Server {server_id} not found"}, ) return dict(row._mapping) def get_adapter_for_server(server_id: int, db: Connection): """Load server and resolve its adapter. Raises 404 if server not found.""" server = get_server_or_404(server_id, db) from adapters.registry import GameAdapterRegistry try: return GameAdapterRegistry.get(server["game_type"]) except KeyError: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail={ "code": "GAME_TYPE_NOT_FOUND", "message": f"No adapter for game type '{server['game_type']}'", }, )