diff --git a/backend/app/scheduler.py b/backend/app/scheduler.py index 893f4e2..c6d7783 100644 --- a/backend/app/scheduler.py +++ b/backend/app/scheduler.py @@ -11,6 +11,7 @@ from app.config import get_settings from app.database import AsyncSessionLocal from app.models.domain import Domain, DomainCheck from app.models.user import User +from app.models.subscription import Subscription, SubscriptionTier, TIER_CONFIG from app.services.domain_checker import domain_checker from app.services.email_service import email_service from app.services.price_tracker import price_tracker @@ -44,14 +45,38 @@ async def scrape_tld_prices(): logger.exception(f"TLD price scrape failed: {e}") -async def check_all_domains(): - """Check availability of all monitored domains.""" - logger.info("Starting daily domain check...") +async def check_domains_by_frequency(frequency: str): + """Check availability of domains based on their subscription frequency. + + Args: + frequency: One of 'daily', 'hourly', 'realtime' (10-min) + """ + logger.info(f"Starting {frequency} domain check...") start_time = datetime.utcnow() async with AsyncSessionLocal() as db: - # Get all domains - result = await db.execute(select(Domain)) + # Get users with matching check frequency + tiers_for_frequency = [] + for tier, config in TIER_CONFIG.items(): + if config['check_frequency'] == frequency: + tiers_for_frequency.append(tier) + # Realtime includes hourly and daily too (more frequent = superset) + elif frequency == 'realtime': + tiers_for_frequency.append(tier) + elif frequency == 'hourly' and config['check_frequency'] in ['hourly', 'realtime']: + tiers_for_frequency.append(tier) + + # Get domains from users with matching subscription tier + from sqlalchemy.orm import joinedload + result = await db.execute( + select(Domain) + .join(User, Domain.user_id == User.id) + .outerjoin(Subscription, Subscription.user_id == User.id) + .where( + (Subscription.tier.in_(tiers_for_frequency)) | + (Subscription.id.is_(None) & (frequency == 'daily')) # Scout users (no subscription) = daily + ) + ) domains = result.scalars().all() logger.info(f"Checking {len(domains)} domains...") @@ -112,14 +137,47 @@ async def check_all_domains(): await send_domain_availability_alerts(db, newly_available) +async def check_all_domains(): + """Legacy function - checks all domains (daily).""" + await check_domains_by_frequency('daily') + + +async def check_hourly_domains(): + """Check domains for Trader users (hourly).""" + await check_domains_by_frequency('hourly') + + +async def check_realtime_domains(): + """Check domains for Tycoon users (every 10 minutes).""" + await check_domains_by_frequency('realtime') + + def setup_scheduler(): """Configure and start the scheduler.""" - # Daily domain check at configured hour + # Daily domain check for Scout users at configured hour scheduler.add_job( check_all_domains, CronTrigger(hour=settings.check_hour, minute=settings.check_minute), id="daily_domain_check", - name="Daily Domain Availability Check", + name="Daily Domain Check (Scout)", + replace_existing=True, + ) + + # Hourly domain check for Trader users + scheduler.add_job( + check_hourly_domains, + CronTrigger(minute=0), # Every hour at :00 + id="hourly_domain_check", + name="Hourly Domain Check (Trader)", + replace_existing=True, + ) + + # 10-minute domain check for Tycoon users + scheduler.add_job( + check_realtime_domains, + CronTrigger(minute='*/10'), # Every 10 minutes + id="realtime_domain_check", + name="10-Minute Domain Check (Tycoon)", replace_existing=True, ) @@ -152,7 +210,9 @@ def setup_scheduler(): logger.info( f"Scheduler configured:" - f"\n - Domain check at {settings.check_hour:02d}:{settings.check_minute:02d}" + f"\n - Scout domain check at {settings.check_hour:02d}:{settings.check_minute:02d} (daily)" + f"\n - Trader domain check every hour at :00" + f"\n - Tycoon domain check every 10 minutes" f"\n - TLD price scrape at 03:00 UTC" f"\n - Price change alerts at 04:00 UTC" f"\n - Auction scrape every hour at :30" diff --git a/frontend/src/app/pricing/page.tsx b/frontend/src/app/pricing/page.tsx index d910e46..1223cc3 100644 --- a/frontend/src/app/pricing/page.tsx +++ b/frontend/src/app/pricing/page.tsx @@ -231,10 +231,7 @@ export default function PricingPage() { {tier.features.map((feature) => (
{tierName} Plan
-- {subscription?.check_frequency || 'Daily'} checks · {subscription?.domain_limit || 5} domains -
-{tierName}
++ {tierName === 'Scout' ? 'Free forever' : tierName === 'Trader' ? '$19/month' : '$49/month'} +
+{subscription?.domain_limit || 5}
+Domains
++ {subscription?.check_frequency === 'realtime' ? '10m' : + subscription?.check_frequency === 'hourly' ? '1h' : '24h'} +
+Check Interval
++ {subscription?.portfolio_limit === -1 ? '∞' : subscription?.portfolio_limit || 0} +
+Portfolio
+| Feature | +Scout | +Trader | +Tycoon | +
|---|---|---|---|
| Price | +Free | +$19/mo | +$49/mo | +
| Watchlist Domains | +5 | +50 | +500 | +
| Scan Frequency | +Daily | +Hourly | +10 min | +
| Portfolio | +— | +25 | +Unlimited | +
| Domain Valuation | +— | +||
| Price History | +— | +90 days | +Unlimited | +
| Expiry Tracking | +— | +