pounce/backend/app/api/deps.py
yves.gugger b795b809f1
Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled
fix: Backend import error + More Tone of Voice updates
BACKEND FIX:
- Added CurrentUserOptional alias to deps.py for backward compatibility

TONE OF VOICE UPDATES:
- Auctions: 'Live Auctions. One Feed. Strike first.'
- Careers: 'Build tools. For hunters.'
- Blog: 'The Hunt Report' - Market intel for hunters
- Settings: 'Your account. Your rules.'
- Forgot Password: Cleaner, direct copy
2025-12-08 16:48:36 +01:00

111 lines
3.1 KiB
Python

"""API dependencies."""
from typing import Annotated, Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.services.auth import AuthService
from app.models.user import User
# Security scheme
security = HTTPBearer()
security_optional = HTTPBearer(auto_error=False)
async def get_current_user(
credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)],
db: Annotated[AsyncSession, Depends(get_db)],
) -> User:
"""Get current authenticated user from JWT token."""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
token = credentials.credentials
payload = AuthService.decode_token(token)
if payload is None:
raise credentials_exception
user_id_str = payload.get("sub")
if user_id_str is None:
raise credentials_exception
try:
user_id = int(user_id_str)
except (ValueError, TypeError):
raise credentials_exception
user = await AuthService.get_user_by_id(db, user_id)
if user is None:
raise credentials_exception
if not user.is_active:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="User account is disabled",
)
return user
async def get_current_active_user(
current_user: Annotated[User, Depends(get_current_user)],
) -> User:
"""Ensure user is active."""
if not current_user.is_active:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Inactive user",
)
return current_user
async def get_current_user_optional(
credentials: Annotated[Optional[HTTPAuthorizationCredentials], Depends(security_optional)],
db: Annotated[AsyncSession, Depends(get_db)],
) -> Optional[User]:
"""Get current user if authenticated, otherwise return None.
This allows endpoints to work for both authenticated and anonymous users,
potentially showing different content based on auth status.
"""
if credentials is None:
return None
token = credentials.credentials
payload = AuthService.decode_token(token)
if payload is None:
return None
user_id_str = payload.get("sub")
if user_id_str is None:
return None
try:
user_id = int(user_id_str)
except (ValueError, TypeError):
return None
user = await AuthService.get_user_by_id(db, user_id)
if user is None or not user.is_active:
return None
return user
# Type aliases for cleaner annotations
CurrentUser = Annotated[User, Depends(get_current_user)]
ActiveUser = Annotated[User, Depends(get_current_active_user)]
OptionalUser = Annotated[Optional[User], Depends(get_current_user_optional)]
CurrentUserOptional = OptionalUser # Alias for backward compatibility
Database = Annotated[AsyncSession, Depends(get_db)]