pounce/backend/app/api/deps.py
yves.gugger e3234e660e fix: Backend deps & transparent valuation schema
FIXED:
- Added get_current_user_optional to deps.py for mixed auth endpoints
- Added OptionalUser type alias for cleaner annotations
- Expanded ValuationResponse schema with full transparency:
  - ValuationScores (length, tld, keyword, brandability, overall)
  - ValuationFactors (length, tld, has_numbers, has_hyphens, etc.)
  - ValuationCalculation (base_value, all factors with reasons, formula)
  - RegistrationContext (tld_cost, value_to_cost_ratio)
  - Added disclaimer field
- Increased base_value from $10 to $50 for realistic valuations

VALUATION EXAMPLES:
- crypto.ai: $375 (was $70)
- x.com: $850 (1-letter premium)
- generic-domain-name.info: $5 (long with hyphens)

All API endpoints tested and working.
2025-12-08 13:51:09 +01:00

110 lines
3.0 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)]
Database = Annotated[AsyncSession, Depends(get_db)]