""" Таблицы: * `clients` — клиенты API («Users» в #17): ключ (только sha256), время создания и последнего обращения. * `windows` — окна браузера: когда открыто и закрыто, последнее обращение, клиент, задача, открывшая окно. Из них считается время, которое браузер был активен. * `requests` — задачи: создание (постановка в очередь), начало и конец выполнения, итог, данные задачи без секретов (прокси, ключ 2captcha, вводимые значения и значения cookies не пишутся). """ from datetime import datetime, timezone from sqlalchemy import JSON, DateTime, ForeignKey, Integer, String from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column def utcnow() -> datetime: return datetime.now(timezone.utc) class Base(DeclarativeBase): type_annotation_map = {datetime: DateTime(timezone=True)} class Client(Base): __tablename__ = "clients" id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String(200)) # sha256 ключа: сам ключ показывается один раз при выпуске и нигде не хранится key_hash: Mapped[str] = mapped_column(String(64), unique=True, index=True) # начало ключа — чтобы отличать ключи в списке key_prefix: Mapped[str] = mapped_column(String(16)) created_at: Mapped[datetime] = mapped_column(default=utcnow) last_access_at: Mapped[datetime | None] revoked_at: Mapped[datetime | None] class Window(Base): __tablename__ = "windows" id: Mapped[int] = mapped_column(Integer, primary_key=True) client_id: Mapped[int | None] = mapped_column(ForeignKey("clients.id"), index=True) session_id: Mapped[str] = mapped_column(String(36), index=True) # задача, открывшая окно task_id: Mapped[str] = mapped_column(String(36)) worker_id: Mapped[str] = mapped_column(String(100)) started_at: Mapped[datetime] = mapped_column(default=utcnow) last_access_at: Mapped[datetime] = mapped_column(default=utcnow) closed_at: Mapped[datetime | None] class Request(Base): __tablename__ = "requests" id: Mapped[int] = mapped_column(Integer, primary_key=True) task_id: Mapped[str] = mapped_column(String(36), unique=True) client_id: Mapped[int | None] = mapped_column(ForeignKey("clients.id"), index=True) session_id: Mapped[str] = mapped_column(String(36), index=True) window_id: Mapped[int | None] = mapped_column(ForeignKey("windows.id")) # api: /v1/solve или /v2/tasks api: Mapped[str] = mapped_column(String(8)) created_at: Mapped[datetime | None] started_at: Mapped[datetime] = mapped_column(default=utcnow, index=True) finished_at: Mapped[datetime | None] duration_ms: Mapped[int | None] # ok / err / failed; code — как в ответе (challenge_failed, action_failed, ...) status: Mapped[str | None] = mapped_column(String(16)) code: Mapped[str | None] = mapped_column(String(32)) task_data: Mapped[dict] = mapped_column(JSON)