pounce/backend/app/models/auction.py
yves.gugger 88eca582e5 feat: Remove ALL mock data - real scraped data only
MOCK DATA REMOVED:
- Removed ALL hardcoded auction data from auctions.py
- Now uses real-time scraping from ExpiredDomains.net
- Database stores scraped auctions (domain_auctions table)
- Scraping runs hourly via scheduler (:30 each hour)

AUCTION SCRAPER SERVICE:
- Web scraping from ExpiredDomains.net (aggregator)
- Rate limiting per platform (10 req/min)
- Database caching to minimize requests
- Cleanup of ended auctions (auto-deactivate)
- Scrape logging for monitoring

STRIPE INTEGRATION:
- Full payment flow: Checkout → Webhook → Subscription update
- Customer Portal for managing subscriptions
- Price IDs configurable via env vars
- Handles: checkout.completed, subscription.updated/deleted, payment.failed

EMAIL SERVICE (SMTP):
- Beautiful HTML email templates with pounce branding
- Domain available alerts
- Price change notifications
- Subscription confirmations
- Weekly digest emails
- Configurable via SMTP_* env vars

NEW SUBSCRIPTION TIERS:
- Scout (Free): 5 domains, daily checks
- Trader (€19/mo): 50 domains, hourly, portfolio, valuation
- Tycoon (€49/mo): 500+ domains, realtime, API, bulk tools

DATABASE CHANGES:
- domain_auctions table for scraped data
- auction_scrape_logs for monitoring
- stripe_customer_id on users
- stripe_subscription_id on subscriptions
- portfolio_domain relationships fixed

ENV VARS ADDED:
- STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET
- STRIPE_PRICE_TRADER, STRIPE_PRICE_TYCOON
- SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD
- SMTP_FROM_EMAIL, SMTP_FROM_NAME
2025-12-08 14:08:52 +01:00

91 lines
4.0 KiB
Python

"""Auction database models for storing scraped auction data."""
from datetime import datetime
from typing import Optional
from sqlalchemy import Boolean, Column, DateTime, Float, Integer, String, Text, Index
from sqlalchemy.orm import Mapped, mapped_column
from app.database import Base
class DomainAuction(Base):
"""
Stores domain auction data scraped from various platforms.
Platforms supported:
- GoDaddy Auctions (auctions.godaddy.com)
- Sedo (sedo.com)
- NameJet (namejet.com)
- Afternic (afternic.com)
- DropCatch (dropcatch.com)
Data is scraped periodically and cached here.
"""
__tablename__ = "domain_auctions"
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
# Domain info
domain: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
tld: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
# Platform info
platform: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
platform_auction_id: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
auction_url: Mapped[str] = mapped_column(Text, nullable=False)
# Pricing
current_bid: Mapped[float] = mapped_column(Float, nullable=False)
currency: Mapped[str] = mapped_column(String(10), default="USD")
min_bid: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
buy_now_price: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
reserve_price: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
reserve_met: Mapped[Optional[bool]] = mapped_column(Boolean, nullable=True)
# Auction details
num_bids: Mapped[int] = mapped_column(Integer, default=0)
num_watchers: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
end_time: Mapped[datetime] = mapped_column(DateTime, nullable=False, index=True)
auction_type: Mapped[str] = mapped_column(String(50), default="auction") # auction, buy_now, offer
# Domain metrics (if available from platform)
traffic: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
age_years: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
backlinks: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
domain_authority: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
# Scraping metadata
scraped_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
scrape_source: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
# Indexes for common queries
__table_args__ = (
Index('ix_auctions_platform_domain', 'platform', 'domain'),
Index('ix_auctions_end_time_active', 'end_time', 'is_active'),
Index('ix_auctions_tld_bid', 'tld', 'current_bid'),
)
def __repr__(self):
return f"<DomainAuction(domain='{self.domain}', platform='{self.platform}', bid=${self.current_bid})>"
class AuctionScrapeLog(Base):
"""Logs scraping activity for monitoring and debugging."""
__tablename__ = "auction_scrape_logs"
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
platform: Mapped[str] = mapped_column(String(100), nullable=False)
started_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
status: Mapped[str] = mapped_column(String(50), default="running") # running, success, failed
auctions_found: Mapped[int] = mapped_column(Integer, default=0)
auctions_updated: Mapped[int] = mapped_column(Integer, default=0)
auctions_new: Mapped[int] = mapped_column(Integer, default=0)
error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
def __repr__(self):
return f"<AuctionScrapeLog(platform='{self.platform}', status='{self.status}')>"