"""Domain models.""" from datetime import datetime from enum import Enum from sqlalchemy import String, Boolean, DateTime, ForeignKey, Text, Enum as SQLEnum from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database import Base class DomainStatus(str, Enum): """Domain availability status.""" AVAILABLE = "available" TAKEN = "taken" ERROR = "error" UNKNOWN = "unknown" class Domain(Base): """Domain model for tracking domain names.""" __tablename__ = "domains" id: Mapped[int] = mapped_column(primary_key=True, index=True) name: Mapped[str] = mapped_column(String(255), index=True, nullable=False) # Current status status: Mapped[DomainStatus] = mapped_column( SQLEnum(DomainStatus), default=DomainStatus.UNKNOWN ) is_available: Mapped[bool] = mapped_column(Boolean, default=False) # WHOIS data (optional) registrar: Mapped[str | None] = mapped_column(String(255), nullable=True) expiration_date: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) # User relationship user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), nullable=False) user: Mapped["User"] = relationship("User", back_populates="domains") # Timestamps created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) last_checked: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) # Check history relationship checks: Mapped[list["DomainCheck"]] = relationship( "DomainCheck", back_populates="domain", cascade="all, delete-orphan" ) # Notification settings notify_on_available: Mapped[bool] = mapped_column(Boolean, default=True) def __repr__(self) -> str: return f"" class DomainCheck(Base): """History of domain availability checks.""" __tablename__ = "domain_checks" id: Mapped[int] = mapped_column(primary_key=True, index=True) domain_id: Mapped[int] = mapped_column(ForeignKey("domains.id"), nullable=False) # Check results status: Mapped[DomainStatus] = mapped_column(SQLEnum(DomainStatus)) is_available: Mapped[bool] = mapped_column(Boolean) # Details response_data: Mapped[str | None] = mapped_column(Text, nullable=True) error_message: Mapped[str | None] = mapped_column(Text, nullable=True) # Timestamp checked_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) # Relationship domain: Mapped["Domain"] = relationship("Domain", back_populates="checks") def __repr__(self) -> str: return f"" class HealthStatus(str, Enum): """Domain health status levels.""" HEALTHY = "healthy" WEAKENING = "weakening" PARKED = "parked" CRITICAL = "critical" UNKNOWN = "unknown" class DomainHealthCache(Base): """ Cached health check results for domains. Updated daily by the scheduler to provide instant health status without needing manual checks. """ __tablename__ = "domain_health_cache" id: Mapped[int] = mapped_column(primary_key=True, index=True) domain_id: Mapped[int] = mapped_column(ForeignKey("domains.id"), unique=True, nullable=False) # Health status status: Mapped[str] = mapped_column(String(20), default="unknown") score: Mapped[int] = mapped_column(default=0) # Signals (JSON array as text) signals: Mapped[str | None] = mapped_column(Text, nullable=True) # Layer data (JSON as text for flexibility) dns_data: Mapped[str | None] = mapped_column(Text, nullable=True) http_data: Mapped[str | None] = mapped_column(Text, nullable=True) ssl_data: Mapped[str | None] = mapped_column(Text, nullable=True) # Timestamp checked_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) # Relationship domain: Mapped["Domain"] = relationship("Domain", backref="health_cache") def __repr__(self) -> str: return f""