| from sqlalchemy import (
|
| Column,
|
| Integer,
|
| String,
|
| Boolean,
|
| Float,
|
| Text,
|
| DateTime,
|
| ForeignKey,
|
| Index,
|
| JSON,
|
| )
|
| from sqlalchemy.orm import relationship
|
| from sqlalchemy.sql import func
|
| from app.database import Base
|
|
|
|
|
| class User(Base):
|
| __tablename__ = "users"
|
|
|
| id = Column(Integer, primary_key=True, index=True)
|
| oauth_provider = Column(String(20), nullable=False)
|
| oauth_id = Column(String(100), nullable=False, unique=True, index=True)
|
| email = Column(String(255), nullable=False)
|
| username = Column(String(100), nullable=False)
|
| avatar_url = Column(Text, nullable=True)
|
| role = Column(String(20), default="user", nullable=False)
|
| created_at = Column(DateTime(timezone=True), server_default=func.now())
|
| last_login = Column(DateTime(timezone=True), nullable=True)
|
|
|
| sources = relationship(
|
| "ProxySource", back_populates="owner", cascade="all, delete-orphan"
|
| )
|
|
|
| __table_args__ = (
|
| Index("idx_oauth_provider_id", "oauth_provider", "oauth_id", unique=True),
|
| )
|
|
|
|
|
| class ProxySource(Base):
|
| __tablename__ = "proxy_sources"
|
|
|
| id = Column(Integer, primary_key=True, index=True)
|
| user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True)
|
| url = Column(Text, nullable=False, unique=True, index=True)
|
| type = Column(String(50), nullable=False)
|
| name = Column(String(200), nullable=True)
|
| description = Column(Text, nullable=True)
|
| is_paid = Column(Boolean, default=False)
|
| enabled = Column(Boolean, default=True, index=True)
|
| validated = Column(Boolean, default=False)
|
| validation_error = Column(Text, nullable=True)
|
| created_at = Column(DateTime(timezone=True), server_default=func.now())
|
| updated_at = Column(
|
| DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
| )
|
| last_scraped = Column(DateTime(timezone=True), nullable=True)
|
| total_scraped = Column(Integer, default=0)
|
| success_rate = Column(Float, default=0.0)
|
| is_admin_source = Column(Boolean, default=False)
|
|
|
| owner = relationship("User", back_populates="sources")
|
| proxies = relationship("Proxy", back_populates="source")
|
|
|
|
|
| class Proxy(Base):
|
| __tablename__ = "proxies"
|
|
|
| id = Column(Integer, primary_key=True, index=True)
|
| source_id = Column(
|
| Integer, ForeignKey("proxy_sources.id", ondelete="SET NULL"), nullable=True
|
| )
|
| url = Column(Text, nullable=False, unique=True, index=True)
|
| protocol = Column(String(50), nullable=False, index=True)
|
| ip = Column(String(50), nullable=True)
|
| port = Column(Integer, nullable=True)
|
| country_code = Column(String(2), nullable=True, index=True)
|
| country_name = Column(String(100), nullable=True)
|
| state = Column(String(100), nullable=True)
|
| city = Column(String(100), nullable=True)
|
|
|
| latency_ms = Column(Integer, nullable=True, index=True)
|
| speed_mbps = Column(Float, nullable=True)
|
| uptime_percent = Column(Float, nullable=True)
|
|
|
| anonymity = Column(String(20), nullable=True, index=True)
|
| proxy_type = Column(String(20), nullable=True)
|
| can_access_google = Column(Boolean, nullable=True)
|
| quality_score = Column(Integer, nullable=True, index=True)
|
|
|
| validation_status = Column(
|
| String(20), default="pending", nullable=False, index=True
|
| )
|
| last_validated = Column(DateTime(timezone=True), nullable=True)
|
| validation_failures = Column(Integer, default=0)
|
| is_working = Column(Boolean, default=True, index=True)
|
|
|
| created_at = Column(DateTime(timezone=True), server_default=func.now())
|
| updated_at = Column(
|
| DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
| )
|
| first_seen = Column(DateTime(timezone=True), server_default=func.now())
|
| last_seen = Column(DateTime(timezone=True), server_default=func.now())
|
|
|
| source = relationship("ProxySource", back_populates="proxies")
|
| validation_history = relationship(
|
| "ValidationHistory", back_populates="proxy", cascade="all, delete-orphan"
|
| )
|
|
|
| __table_args__ = (
|
| Index(
|
| "idx_proxy_working_status_quality",
|
| "is_working",
|
| "validation_status",
|
| "quality_score",
|
| ),
|
| )
|
|
|
|
|
| class ValidationHistory(Base):
|
| __tablename__ = "validation_history"
|
|
|
| id = Column(Integer, primary_key=True, index=True)
|
| proxy_id = Column(
|
| Integer,
|
| ForeignKey("proxies.id", ondelete="CASCADE"),
|
| nullable=False,
|
| index=True,
|
| )
|
| validated_at = Column(
|
| DateTime(timezone=True), server_default=func.now(), index=True
|
| )
|
| latency_ms = Column(Integer, nullable=True)
|
| anonymity = Column(String(20), nullable=True)
|
| can_access_google = Column(Boolean, nullable=True)
|
| success = Column(Boolean, nullable=False)
|
| error_message = Column(Text, nullable=True)
|
|
|
| proxy = relationship("Proxy", back_populates="validation_history")
|
|
|
|
|
| class CandidateSource(Base):
|
| __tablename__ = "candidate_sources"
|
|
|
| id = Column(Integer, primary_key=True, index=True)
|
| url = Column(Text, nullable=False, unique=True, index=True)
|
| domain = Column(String(255), nullable=True, index=True)
|
| discovery_method = Column(String(50), nullable=False)
|
| status = Column(
|
| String(20), default="pending", nullable=False, index=True
|
| )
|
| confidence_score = Column(Integer, default=0, index=True)
|
| proxies_found_count = Column(Integer, default=0)
|
| last_checked_at = Column(DateTime(timezone=True), nullable=True)
|
| created_at = Column(DateTime(timezone=True), server_default=func.now())
|
| fail_count = Column(Integer, default=0)
|
| meta_data = Column(JSON, nullable=True)
|
|
|
| __table_args__ = (
|
| Index("idx_candidate_status_score", "status", "confidence_score"),
|
| )
|
|
|
|
|
| class Notification(Base):
|
| __tablename__ = "notifications"
|
|
|
| id = Column(Integer, primary_key=True, index=True)
|
| user_id = Column(
|
| Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True
|
| )
|
| type = Column(String(50), nullable=False, index=True)
|
| title = Column(String(255), nullable=False)
|
| message = Column(Text, nullable=False)
|
| severity = Column(String(20), default="info", nullable=False)
|
| created_at = Column(DateTime(timezone=True), server_default=func.now(), index=True)
|
| read = Column(Boolean, default=False, index=True)
|
|
|
| user = relationship("User", backref="notifications")
|
|
|
|
|
| class ScrapingSession(Base):
|
| __tablename__ = "scraping_sessions"
|
|
|
| id = Column(Integer, primary_key=True, index=True)
|
| source_id = Column(
|
| Integer,
|
| ForeignKey("proxy_sources.id", ondelete="CASCADE"),
|
| nullable=False,
|
| index=True,
|
| )
|
| scraping_type = Column(String(50), default="scheduled", nullable=False)
|
| status = Column(String(20), default="running", nullable=False, index=True)
|
| proxies_found = Column(Integer, default=0)
|
| proxies_valid = Column(Integer, default=0)
|
| config = Column(JSON, nullable=True)
|
| error_message = Column(Text, nullable=True)
|
| initiated_by = Column(
|
| Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True
|
| )
|
| started_at = Column(DateTime(timezone=True), server_default=func.now(), index=True)
|
| finished_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
| source = relationship("ProxySource")
|
| initiator = relationship("User")
|
|
|
| __table_args__ = (
|
| Index("idx_scraping_session_status_source", "status", "source_id"),
|
| )
|
|
|
|
|
| class BackgroundTask(Base):
|
| __tablename__ = "background_tasks"
|
|
|
| id = Column(Integer, primary_key=True, index=True)
|
| task_type = Column(String(100), nullable=False, index=True)
|
| task_data = Column(JSON, nullable=False)
|
| status = Column(String(20), default="pending", nullable=False, index=True)
|
| result = Column(JSON, nullable=True)
|
| error_message = Column(Text, nullable=True)
|
| retry_count = Column(Integer, default=0)
|
| max_retries = Column(Integer, default=3)
|
| scheduled_for = Column(
|
| DateTime(timezone=True), server_default=func.now(), index=True
|
| )
|
| started_at = Column(DateTime(timezone=True), nullable=True)
|
| completed_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
| __table_args__ = (
|
| Index("idx_background_task_status_scheduled", "status", "scheduled_for"),
|
| )
|
|
|