106 lines
3.2 KiB
Python
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(),
|
|
}
|
|
|
|
|