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
Backend: - Add YieldDomain, YieldTransaction, YieldPayout, AffiliatePartner models - Create IntentDetector service for keyword-based intent classification - Implement /api/v1/yield/* endpoints (dashboard, domains, transactions, partners) - Support domain activation, DNS verification, and revenue tracking Frontend: - Add /terminal/yield page with dashboard and activate wizard - Add YIELD to sidebar navigation under 'Monetize' section - Add 4th pillar 'Yield' to landing page 'Beyond Hunting' section - Extend API client with yield endpoints and types Features: - AI-powered intent detection (medical, finance, legal, realestate, etc.) - Swiss/German geo-targeting with city recognition - Revenue estimation based on intent category and geo - DNS verification via nameservers or CNAME - 70/30 revenue split tracking
285 lines
7.0 KiB
Python
285 lines
7.0 KiB
Python
"""
|
|
Pydantic schemas for Yield/Intent Routing feature.
|
|
"""
|
|
from datetime import datetime
|
|
from decimal import Decimal
|
|
from typing import Optional
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
# ============================================================================
|
|
# Intent Detection
|
|
# ============================================================================
|
|
|
|
class IntentAnalysis(BaseModel):
|
|
"""Intent detection result for a domain."""
|
|
category: str
|
|
subcategory: Optional[str] = None
|
|
confidence: float = Field(ge=0.0, le=1.0)
|
|
keywords_matched: list[str] = []
|
|
suggested_partners: list[str] = []
|
|
monetization_potential: str # "high", "medium", "low"
|
|
|
|
|
|
class YieldValueEstimate(BaseModel):
|
|
"""Estimated yield value for a domain."""
|
|
estimated_monthly_min: int
|
|
estimated_monthly_max: int
|
|
currency: str = "CHF"
|
|
potential: str
|
|
confidence: float
|
|
geo: Optional[str] = None
|
|
|
|
|
|
class DomainYieldAnalysis(BaseModel):
|
|
"""Complete yield analysis for a domain."""
|
|
domain: str
|
|
intent: IntentAnalysis
|
|
value: YieldValueEstimate
|
|
partners: list[str] = []
|
|
monetization_potential: str
|
|
|
|
|
|
# ============================================================================
|
|
# Yield Domain CRUD
|
|
# ============================================================================
|
|
|
|
class YieldDomainCreate(BaseModel):
|
|
"""Create a new yield domain."""
|
|
domain: str = Field(..., min_length=3, max_length=255)
|
|
|
|
|
|
class YieldDomainUpdate(BaseModel):
|
|
"""Update yield domain settings."""
|
|
active_route: Optional[str] = None
|
|
landing_page_url: Optional[str] = None
|
|
status: Optional[str] = None
|
|
|
|
|
|
class YieldDomainResponse(BaseModel):
|
|
"""Yield domain response."""
|
|
id: int
|
|
domain: str
|
|
status: str
|
|
|
|
# Intent
|
|
detected_intent: Optional[str] = None
|
|
intent_confidence: float = 0.0
|
|
|
|
# Routing
|
|
active_route: Optional[str] = None
|
|
partner_name: Optional[str] = None
|
|
|
|
# DNS
|
|
dns_verified: bool = False
|
|
dns_verified_at: Optional[datetime] = None
|
|
|
|
# Stats
|
|
total_clicks: int = 0
|
|
total_conversions: int = 0
|
|
total_revenue: Decimal = Decimal("0")
|
|
currency: str = "CHF"
|
|
|
|
# Timestamps
|
|
activated_at: Optional[datetime] = None
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class YieldDomainListResponse(BaseModel):
|
|
"""List of yield domains with summary stats."""
|
|
domains: list[YieldDomainResponse]
|
|
total: int
|
|
|
|
# Aggregates
|
|
total_active: int = 0
|
|
total_revenue: Decimal = Decimal("0")
|
|
total_clicks: int = 0
|
|
|
|
|
|
# ============================================================================
|
|
# Transactions
|
|
# ============================================================================
|
|
|
|
class YieldTransactionResponse(BaseModel):
|
|
"""Single transaction record."""
|
|
id: int
|
|
event_type: str
|
|
partner_slug: str
|
|
|
|
gross_amount: Decimal
|
|
net_amount: Decimal
|
|
currency: str
|
|
|
|
status: str
|
|
geo_country: Optional[str] = None
|
|
|
|
created_at: datetime
|
|
confirmed_at: Optional[datetime] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class YieldTransactionListResponse(BaseModel):
|
|
"""List of transactions."""
|
|
transactions: list[YieldTransactionResponse]
|
|
total: int
|
|
|
|
# Aggregates
|
|
total_gross: Decimal = Decimal("0")
|
|
total_net: Decimal = Decimal("0")
|
|
|
|
|
|
# ============================================================================
|
|
# Payouts
|
|
# ============================================================================
|
|
|
|
class YieldPayoutResponse(BaseModel):
|
|
"""Payout record."""
|
|
id: int
|
|
amount: Decimal
|
|
currency: str
|
|
|
|
period_start: datetime
|
|
period_end: datetime
|
|
|
|
transaction_count: int
|
|
status: str
|
|
|
|
payment_method: Optional[str] = None
|
|
payment_reference: Optional[str] = None
|
|
|
|
created_at: datetime
|
|
completed_at: Optional[datetime] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class YieldPayoutListResponse(BaseModel):
|
|
"""List of payouts."""
|
|
payouts: list[YieldPayoutResponse]
|
|
total: int
|
|
total_paid: Decimal = Decimal("0")
|
|
total_pending: Decimal = Decimal("0")
|
|
|
|
|
|
# ============================================================================
|
|
# Dashboard
|
|
# ============================================================================
|
|
|
|
class YieldDashboardStats(BaseModel):
|
|
"""Yield dashboard statistics."""
|
|
# Domain counts
|
|
total_domains: int = 0
|
|
active_domains: int = 0
|
|
pending_domains: int = 0
|
|
|
|
# Revenue (current month)
|
|
monthly_revenue: Decimal = Decimal("0")
|
|
monthly_clicks: int = 0
|
|
monthly_conversions: int = 0
|
|
|
|
# Lifetime
|
|
lifetime_revenue: Decimal = Decimal("0")
|
|
lifetime_clicks: int = 0
|
|
lifetime_conversions: int = 0
|
|
|
|
# Pending payout
|
|
pending_payout: Decimal = Decimal("0")
|
|
next_payout_date: Optional[datetime] = None
|
|
|
|
currency: str = "CHF"
|
|
|
|
|
|
class YieldDashboardResponse(BaseModel):
|
|
"""Complete yield dashboard data."""
|
|
stats: YieldDashboardStats
|
|
domains: list[YieldDomainResponse]
|
|
recent_transactions: list[YieldTransactionResponse]
|
|
top_domains: list[YieldDomainResponse]
|
|
|
|
|
|
# ============================================================================
|
|
# Partners
|
|
# ============================================================================
|
|
|
|
class AffiliatePartnerResponse(BaseModel):
|
|
"""Affiliate partner info (public view)."""
|
|
slug: str
|
|
name: str
|
|
network: str
|
|
|
|
intent_categories: list[str]
|
|
geo_countries: list[str]
|
|
|
|
payout_type: str
|
|
description: Optional[str] = None
|
|
logo_url: Optional[str] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# ============================================================================
|
|
# DNS Verification
|
|
# ============================================================================
|
|
|
|
class DNSVerificationResult(BaseModel):
|
|
"""Result of DNS verification check."""
|
|
domain: str
|
|
verified: bool
|
|
|
|
expected_ns: list[str]
|
|
actual_ns: list[str]
|
|
|
|
cname_ok: bool = False
|
|
|
|
error: Optional[str] = None
|
|
checked_at: datetime
|
|
|
|
|
|
class DNSSetupInstructions(BaseModel):
|
|
"""DNS setup instructions for a domain."""
|
|
domain: str
|
|
|
|
# Option 1: Nameserver delegation
|
|
nameservers: list[str]
|
|
|
|
# Option 2: CNAME
|
|
cname_host: str
|
|
cname_target: str
|
|
|
|
# Verification
|
|
verification_url: str
|
|
|
|
|
|
# ============================================================================
|
|
# Activation Flow
|
|
# ============================================================================
|
|
|
|
class ActivateYieldRequest(BaseModel):
|
|
"""Request to activate a domain for yield."""
|
|
domain: str = Field(..., min_length=3, max_length=255)
|
|
accept_terms: bool = False
|
|
|
|
|
|
class ActivateYieldResponse(BaseModel):
|
|
"""Response after initiating yield activation."""
|
|
domain_id: int
|
|
domain: str
|
|
status: str
|
|
|
|
# Analysis
|
|
intent: IntentAnalysis
|
|
value_estimate: YieldValueEstimate
|
|
|
|
# Setup
|
|
dns_instructions: DNSSetupInstructions
|
|
|
|
message: str
|
|
|