pounce/backend/app/api/dashboard.py

106 lines
3.2 KiB
Python

"""Dashboard summary endpoints (reduce frontend API round-trips)."""
from datetime import datetime, timedelta
from fastapi import APIRouter, Depends
from sqlalchemy import select, func, and_
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import get_current_user
from app.database import get_db
from app.models.auction import DomainAuction
from app.models.listing import DomainListing, ListingStatus
from app.models.user import User
# Reuse helpers for consistent formatting
from app.api.auctions import _format_time_remaining, _get_affiliate_url
from app.api.tld_prices import get_trending_tlds
router = APIRouter()
@router.get("/summary")
async def get_dashboard_summary(
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""
Return a compact dashboard payload used by `/terminal/radar`.
Goal: 1 request instead of multiple heavy round-trips.
"""
now = datetime.utcnow()
# -------------------------
# Market stats + preview
# -------------------------
active_auctions_filter = and_(DomainAuction.is_active == True, DomainAuction.end_time > now)
total_auctions = (await db.execute(select(func.count(DomainAuction.id)).where(active_auctions_filter))).scalar() or 0
cutoff = now + timedelta(hours=24)
ending_soon_filter = and_(
DomainAuction.is_active == True,
DomainAuction.end_time > now,
DomainAuction.end_time <= cutoff,
)
ending_soon_count = (await db.execute(select(func.count(DomainAuction.id)).where(ending_soon_filter))).scalar() or 0
ending_soon = (
await db.execute(
select(DomainAuction)
.where(ending_soon_filter)
.order_by(DomainAuction.end_time.asc())
.limit(5)
)
).scalars().all()
ending_soon_preview = [
{
"domain": a.domain,
"current_bid": a.current_bid,
"time_remaining": _format_time_remaining(a.end_time, now=now),
"platform": a.platform,
"affiliate_url": _get_affiliate_url(a.platform, a.domain, a.auction_url),
}
for a in ending_soon
]
# -------------------------
# Listings stats (user)
# -------------------------
listing_counts = (
await db.execute(
select(DomainListing.status, func.count(DomainListing.id))
.where(DomainListing.user_id == current_user.id)
.group_by(DomainListing.status)
)
).all()
by_status = {status: int(count) for status, count in listing_counts}
listing_stats = {
"active": by_status.get(ListingStatus.ACTIVE.value, 0),
"sold": by_status.get(ListingStatus.SOLD.value, 0),
"draft": by_status.get(ListingStatus.DRAFT.value, 0),
"total": sum(by_status.values()),
}
# -------------------------
# Trending TLDs (public data)
# -------------------------
trending = await get_trending_tlds(db)
return {
"market": {
"total_auctions": total_auctions,
"ending_soon": ending_soon_count,
"ending_soon_preview": ending_soon_preview,
},
"listings": listing_stats,
"tlds": trending,
"timestamp": now.isoformat(),
}