pounce/backend/app/api/subscription.py
yves.gugger f0cc69ac95 feat: TLD price scraper, .ch domain fix, DB integration
Major changes:
- Add TLD price scraper with Porkbun API (886+ TLDs, no API key needed)
- Fix .ch domain checker using rdap.nic.ch custom RDAP
- Integrate database for TLD price history tracking
- Add admin endpoints for manual scrape and stats
- Extend scheduler with daily TLD price scrape job (03:00 UTC)
- Update API to use DB data with static fallback
- Update README with complete documentation

New files:
- backend/app/services/tld_scraper/ (scraper package)
- TLD_TRACKING_PLAN.md (implementation plan)

API changes:
- POST /admin/scrape-tld-prices - trigger manual scrape
- GET /admin/tld-prices/stats - database statistics
- GET /tld-prices/overview now uses DB data
2025-12-08 09:12:44 +01:00

131 lines
4.2 KiB
Python

"""Subscription API endpoints."""
from fastapi import APIRouter, HTTPException, status
from sqlalchemy import select, func
from app.api.deps import Database, CurrentUser
from app.models.domain import Domain
from app.models.subscription import Subscription, SubscriptionTier, TIER_CONFIG
from app.schemas.subscription import SubscriptionResponse, SubscriptionTierInfo
router = APIRouter()
@router.get("", response_model=SubscriptionResponse)
async def get_subscription(
current_user: CurrentUser,
db: Database,
):
"""Get current user's subscription details."""
result = await db.execute(
select(Subscription).where(Subscription.user_id == current_user.id)
)
subscription = result.scalar_one_or_none()
if not subscription:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="No subscription found",
)
# Count domains used
domain_count = await db.execute(
select(func.count(Domain.id)).where(Domain.user_id == current_user.id)
)
domains_used = domain_count.scalar()
# Get tier config
config = subscription.config
return SubscriptionResponse(
id=subscription.id,
tier=subscription.tier.value,
tier_name=config["name"],
status=subscription.status.value,
domain_limit=subscription.max_domains,
domains_used=domains_used,
check_frequency=config["check_frequency"],
history_days=config["history_days"],
features=config["features"],
started_at=subscription.started_at,
expires_at=subscription.expires_at,
)
@router.get("/tiers")
async def get_subscription_tiers():
"""Get available subscription tiers and their features."""
tiers = []
for tier_enum, config in TIER_CONFIG.items():
feature_list = []
# Build feature list for display
feature_list.append(f"{config['domain_limit']} domains in watchlist")
if config["check_frequency"] == "hourly":
feature_list.append("Hourly availability checks")
else:
feature_list.append("Daily availability checks")
if config["features"]["priority_alerts"]:
feature_list.append("Priority email notifications")
else:
feature_list.append("Email notifications")
if config["features"]["full_whois"]:
feature_list.append("Full WHOIS data")
else:
feature_list.append("Basic WHOIS data")
if config["history_days"] == -1:
feature_list.append("Unlimited check history")
elif config["history_days"] > 0:
feature_list.append(f"{config['history_days']}-day check history")
if config["features"]["expiration_tracking"]:
feature_list.append("Expiration date tracking")
if config["features"]["api_access"]:
feature_list.append("REST API access")
if config["features"]["webhooks"]:
feature_list.append("Webhook integrations")
tiers.append({
"id": tier_enum.value,
"name": config["name"],
"domain_limit": config["domain_limit"],
"price": config["price"],
"check_frequency": config["check_frequency"],
"features": feature_list,
"feature_flags": config["features"],
})
return {"tiers": tiers}
@router.get("/features")
async def get_my_features(current_user: CurrentUser, db: Database):
"""Get current user's available features based on subscription."""
result = await db.execute(
select(Subscription).where(Subscription.user_id == current_user.id)
)
subscription = result.scalar_one_or_none()
if not subscription:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="No subscription found",
)
config = subscription.config
return {
"tier": subscription.tier.value,
"tier_name": config["name"],
"domain_limit": config["domain_limit"],
"check_frequency": config["check_frequency"],
"history_days": config["history_days"],
"features": config["features"],
}