pounce/backend/app/models/subscription.py
Yves Gugger 9acb90c067 Initial commit: Pounce - Domain Monitoring System
- FastAPI backend mit Domain-Check, TLD-Pricing, User-Management
- Next.js frontend mit modernem UI
- Sortierbare TLD-Tabelle mit Mini-Charts
- Domain availability monitoring
- Subscription tiers (Starter, Professional, Enterprise)
- Authentication & Authorization
- Scheduler für automatische Domain-Checks
2025-12-08 07:26:57 +01:00

140 lines
4.3 KiB
Python

"""Subscription model."""
from datetime import datetime
from enum import Enum
from sqlalchemy import String, DateTime, ForeignKey, Integer, Boolean, Enum as SQLEnum
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
class SubscriptionTier(str, Enum):
"""Subscription tiers matching frontend pricing."""
STARTER = "starter" # Free
PROFESSIONAL = "professional" # $4.99/mo
ENTERPRISE = "enterprise" # $9.99/mo
class SubscriptionStatus(str, Enum):
"""Subscription status."""
ACTIVE = "active"
CANCELLED = "cancelled"
EXPIRED = "expired"
PENDING = "pending"
# Plan configuration
TIER_CONFIG = {
SubscriptionTier.STARTER: {
"name": "Starter",
"price": 0,
"domain_limit": 3,
"check_frequency": "daily", # daily, hourly
"history_days": 0, # No history
"features": {
"email_alerts": True,
"priority_alerts": False,
"full_whois": False,
"expiration_tracking": False,
"api_access": False,
"webhooks": False,
}
},
SubscriptionTier.PROFESSIONAL: {
"name": "Professional",
"price": 4.99,
"domain_limit": 25,
"check_frequency": "daily",
"history_days": 30,
"features": {
"email_alerts": True,
"priority_alerts": True,
"full_whois": True,
"expiration_tracking": True,
"api_access": False,
"webhooks": False,
}
},
SubscriptionTier.ENTERPRISE: {
"name": "Enterprise",
"price": 9.99,
"domain_limit": 100,
"check_frequency": "hourly",
"history_days": -1, # Unlimited
"features": {
"email_alerts": True,
"priority_alerts": True,
"full_whois": True,
"expiration_tracking": True,
"api_access": True,
"webhooks": True,
}
},
}
class Subscription(Base):
"""Subscription model for tracking user plans."""
__tablename__ = "subscriptions"
id: Mapped[int] = mapped_column(primary_key=True, index=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), unique=True, nullable=False)
# Plan details
tier: Mapped[SubscriptionTier] = mapped_column(
SQLEnum(SubscriptionTier), default=SubscriptionTier.STARTER
)
status: Mapped[SubscriptionStatus] = mapped_column(
SQLEnum(SubscriptionStatus), default=SubscriptionStatus.ACTIVE
)
# Limits
domain_limit: Mapped[int] = mapped_column(Integer, default=3)
# Payment info (for future integration)
payment_reference: Mapped[str | None] = mapped_column(String(255), nullable=True)
# Dates
started_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
expires_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
cancelled_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
# Relationship
user: Mapped["User"] = relationship("User", back_populates="subscription")
@property
def is_active(self) -> bool:
"""Check if subscription is currently active."""
if self.status != SubscriptionStatus.ACTIVE:
return False
if self.expires_at and self.expires_at < datetime.utcnow():
return False
return True
@property
def config(self) -> dict:
"""Get configuration for this subscription tier."""
return TIER_CONFIG.get(self.tier, TIER_CONFIG[SubscriptionTier.STARTER])
@property
def max_domains(self) -> int:
"""Get maximum allowed domains for this subscription."""
return self.config["domain_limit"]
@property
def check_frequency(self) -> str:
"""Get check frequency for this subscription."""
return self.config["check_frequency"]
@property
def history_days(self) -> int:
"""Get history retention days. -1 = unlimited."""
return self.config["history_days"]
def has_feature(self, feature: str) -> bool:
"""Check if subscription has a specific feature."""
return self.config.get("features", {}).get(feature, False)
def __repr__(self) -> str:
return f"<Subscription {self.tier.value} for user {self.user_id}>"