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.
This commit is contained in:
yves.gugger
2025-12-08 13:51:09 +01:00
parent f3ac90bb83
commit e3234e660e
3 changed files with 92 additions and 5 deletions

View File

@ -1,5 +1,5 @@
"""API dependencies."""
from typing import Annotated
from typing import Annotated, Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
@ -11,6 +11,7 @@ from app.models.user import User
# Security scheme
security = HTTPBearer()
security_optional = HTTPBearer(auto_error=False)
async def get_current_user(
@ -65,8 +66,44 @@ async def get_current_active_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)]

View File

@ -91,16 +91,63 @@ class PortfolioSummary(BaseModel):
overall_roi: float
class ValuationScores(BaseModel):
"""Domain valuation scores breakdown."""
length: int
tld: int
keyword: int
brandability: int
overall: int
class ValuationFactors(BaseModel):
"""Domain valuation factors."""
length: int
tld: str
has_numbers: bool
has_hyphens: bool
is_dictionary_word: bool
detected_keywords: List[str] = []
class ValuationCalculation(BaseModel):
"""Transparent calculation breakdown."""
base_value: float
length_factor: float
length_reason: str
tld_factor: float
tld_reason: str
keyword_factor: float
keyword_reason: str
brand_factor: float
brand_reason: str
formula: str
raw_result: float
class RegistrationContext(BaseModel):
"""TLD registration cost context."""
tld_cost: Optional[float] = None
value_to_cost_ratio: Optional[float] = None
class ValuationResponse(BaseModel):
"""Response schema for domain valuation."""
"""Response schema for domain valuation - fully transparent."""
domain: str
estimated_value: float
currency: str
scores: dict
factors: dict
confidence: str
# Detailed breakdowns
scores: ValuationScores
factors: ValuationFactors
calculation: ValuationCalculation
registration_context: RegistrationContext
# Metadata
source: str
calculated_at: str
disclaimer: str
# ============== Portfolio Endpoints ==============

View File

@ -144,7 +144,10 @@ class DomainValuationService:
"""
def __init__(self):
self.base_value = 10 # Base value in USD
# Base value calibrated to market research
# A generic 10-char .com domain with no keywords typically sells for ~$50-100
# Our formula: $50 × factors should produce realistic values
self.base_value = 50 # Base value in USD
async def estimate_value(
self,