feature: архитектура v2 — api + очередь + worker, удержание окна браузера (#78)
- Сервис api (FastAPI, без Chrome): POST /v2/tasks, GET /v2/tasks/{id} (доступ по cookie
сессии), DELETE /v2/sessions/current; /v1/solve — прежний контракт поверх очереди.
- Сервис worker (dramatiq + RabbitMQ): слоты браузера, окна сессий между задачами
(browser_idle_timeout 0–120 мин, простой — TTL ключа в Redis), heartbeat.
- Маршрутизация задач сессии в персональную очередь воркера с открытым окном.
- Redis: задачи, результаты (RESULT_TTL_HOURS), окна, воркеры. Прокси и ключ 2captcha
в Redis не сохраняются.
- Движок: AsyncStealthySession.run() — действия на текущей странице без навигации.
- Два образа (robotex-api, robotex-worker), compose с rabbitmq и redis,
make up/down/logs/smoke, релиз собирает все образы.
- Тесты: api + worker в одном процессе (fakeredis), настоящий воркер dramatiq на
StubBroker, браузерные тесты на новой схеме, smoke для запущенного стенда.
- Документация: README, API (v2), Deployment, Usage.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fe509c25c3
commit
7b511d5d38
38 files changed
+2142
-545
No files matched your search
+32
-1
@@ -1,9 +1,11 @@
|
||||
from typing import Any, Literal
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
from pydantic import BaseModel, field_validator, model_validator
|
||||
from pydantic import BaseModel, Field, field_validator, model_validator
|
||||
|
||||
CAPTCHA_TYPES = ("cloudflare", "recaptcha", "hcaptcha")
|
||||
# максимум browser_idle_timeout в минутах: открытое окно держит слот браузера (#78)
|
||||
MAX_BROWSER_IDLE_TIMEOUT = 120
|
||||
PROXY_SCHEMES = ("http", "https", "socks5")
|
||||
|
||||
# тип действия -> обязательные поля (кроме type)
|
||||
@@ -143,3 +145,32 @@ class SolveResponse(BaseModel):
|
||||
html: str | None = None
|
||||
screenshot: str | None = None
|
||||
duration_ms: int | None = None
|
||||
|
||||
|
||||
class TaskRequest(SolveRequest):
|
||||
"""
|
||||
Задача POST /v2/tasks: тело как у /v1/solve плюс удержание окна браузера.
|
||||
|
||||
url необязателен, если окно сессии открыто: тогда действия выполняются на текущей странице.
|
||||
"""
|
||||
url: str | None = None
|
||||
# минуты простоя, через которые окно сессии закроется; 0 — закрыть сразу после задачи
|
||||
browser_idle_timeout: int = Field(0, ge=0, le=MAX_BROWSER_IDLE_TIMEOUT)
|
||||
|
||||
|
||||
class TaskCreated(BaseModel):
|
||||
task_id: str
|
||||
status: str
|
||||
url: str
|
||||
|
||||
|
||||
class TaskResponse(BaseModel):
|
||||
"""
|
||||
GET /v2/tasks/{id}. status: queued → running → done | failed.
|
||||
done — результат в result (в нём свой status ok/err, как в /v1/solve);
|
||||
failed — задачу не удалось выполнить (воркер упал), причина в error.
|
||||
"""
|
||||
task_id: str
|
||||
status: Literal["queued", "running", "done", "failed", "cancelled"]
|
||||
error: str | None = None
|
||||
result: SolveResponse | None = None
|
||||
Reference in new issue
Block a user