From 08a6b52b9d0291a5598cf6016abdd6d1c869a565 Mon Sep 17 00:00:00 2001 From: protokey Date: Sun, 11 Oct 2026 00:17:28 +0400 Subject: [PATCH] =?UTF-8?q?build:=20=D1=81=D0=B1=D0=BE=D1=80=D0=BA=D0=B0?= =?UTF-8?q?=20=D0=B2=20docker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dockerfile: python 3.12, Google Chrome (patchright), Xvfb, tini как PID 1 против зомби-процессов - docker-compose: volume для extra/, shm_size 2gb, порт через API_PORT - requirements: добавлены playwright и camoufox - docs: Deployment.md, README и доки актуализированы под Chrome Co-Authored-By: Claude Opus 5.5 --- .dockerignore | 10 +++++++ Dockerfile | 38 ++++++++++++++++++++++++ README.md | 48 ++++++++++++++++++------------ docker-compose.yml | 17 +++++++++++ docs/AntiBot-Bypass-TZ.md | 4 +-- docs/Deployment.md | 62 +++++++++++++++++++++++++++++++++++++++ docs/Introduction.md | 2 +- requirements.txt | 2 ++ 8 files changed, 162 insertions(+), 21 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 docs/Deployment.md diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..849fd7c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +.git +.idea +.venv +wheels +extra +docs +tests +**/__pycache__ +*.pyc +.env diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7d37b54 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,38 @@ +FROM python:3.12-slim-bookworm + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 + +# Xvfb — браузер запускается в headful-режиме (headless=False), нужен виртуальный дисплей +RUN apt-get update \ + && apt-get install -y --no-install-recommends tini xvfb xauth fonts-liberation fonts-noto-color-emoji ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY requirements.txt . +RUN pip install -r requirements.txt + +# real_chrome=True → channel "chrome": ставим Google Chrome stable вместе с системными зависимостями +RUN patchright install --with-deps chrome \ + && rm -rf /var/lib/apt/lists/* + +RUN useradd --create-home --uid 1000 app \ + && mkdir -p /app/extra \ + && chown -R app:app /app + +COPY --chown=app:app src/ ./src/ + +USER app +WORKDIR /app/src + +# профили браузера (extra/user_data_dir) и geoip-кэш — в volume +VOLUME ["/app/extra"] +EXPOSE 8000 + +# tini как PID 1: пробрасывает сигналы и подбирает осиротевшие процессы Chrome/Xvfb (иначе — зомби) +ENTRYPOINT ["/usr/bin/tini", "-g", "--"] +CMD ["xvfb-run", "-a", "--server-args=-screen 0 1920x1080x24", \ + "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/README.md b/README.md index 1662b0b..0936c25 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,15 @@ Early MVP (v0), under active development. Single browser engine, no queue, no bi Client → Caddy (TLS) → FastAPI → API-key auth → semaphore acquire │ ▼ - Camoufox browser context + Chrome (Patchright) context (run actions, collect result) │ ▼ release semaphore → response ``` -- **Engine**: [Camoufox](https://camoufox.com/) (stealth Firefox, driven via Playwright). Chosen for v1 because - Firefox-based fingerprinting is less commoditized against anti-bot vendors than patched Chromium. +- **Engine**: real Google Chrome (`channel="chrome"`) driven via [Patchright](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright-python) + (undetected Playwright fork), headful under Xvfb. Firefox is not supported yet. - **API**: FastAPI, single versioned endpoint (`POST /v1/solve`). - **Concurrency**: bounded by an `asyncio.Semaphore` over browser contexts — no external job queue in v1. Simpler to run, hard ceiling on throughput per instance (see Roadmap for scaling out). @@ -29,28 +29,30 @@ Client → Caddy (TLS) → FastAPI → API-key auth → semaphore acquire - **Logging**: every request's outcome (success/failure/duration) is persisted — this becomes the success-rate data used to evaluate the service and talk to clients about reliability. - **Proxies**: not managed by the service in v1 — the client supplies their own proxy per request if they need one. -- **Deployment**: single Docker container behind a Caddy reverse proxy. +- **Deployment**: single Docker container behind a Caddy reverse proxy — see [Docker](#docker) and + [docs/Deployment.md](docs/Deployment.md). - **Canvas fingerprint** по дизайну генерируется каждую сессию новый. -### Planned project layout +### Project layout ``` src/ - main.py # FastAPI app + router wiring - config.py # settings (env vars) + main.py # FastAPI app, POST /v1/solve api/ - solve.py # POST /v1/solve - deps.py # API-key auth dependency - engine/ - browser.py # Camoufox context pool + semaphore actions.py # fill / click / wait_for execution - schemas.py # SolveRequest / SolveResponse / Action models - db/ - models.py # api_keys, request_logs - session.py + schemas.py # SolveRequest / Action models + geoip.py # proxy → locale / timezone + engine/ + stealthy.py # AsyncStealthySession (Patchright + Chrome) + session.py # browser / context launch options + page_pool.py # page pool + antibot/ # anti-bot detection & bypass orchestrator (Cloudflare, reCAPTCHA) + captcha/ # captcha solvers (Turnstile, reCAPTCHA, hCaptcha) tests/ wheels/ # whl библиотеки для оффлайн установки под Ubuntu Noble Dockerfile +docker-compose.yml +.dockerignore requirements.txt env.example ``` @@ -74,7 +76,7 @@ X-API-Key: "proxy": "socks5://user:pass@host:port", "screen": "1280x920", - "user_agent": "Firefox", + "user_agent": "Mozilla/5.0 (...) Chrome/...", "timeout": 120 } ``` @@ -131,7 +133,7 @@ Deliberately out of scope for v1: - Job queue (Redis/RabbitMQ) + multiple worker VPS for horizontal scaling - Real billing/usage plans beyond a flat API-key check - Broader action vocabulary beyond fill/click/wait_for -- Second engine (Patchright/Chromium) for targets where the Firefox fingerprint doesn't fit +- Second engine (Firefox, e.g. Camoufox) for targets where the Chrome fingerprint doesn't fit ## Local development @@ -139,5 +141,15 @@ Deliberately out of scope for v1: python -m venv .venv source .venv/bin/activate make sync-offline -uvicorn app.main:app --reload +cd src && uvicorn main:app --reload ``` + +## Docker + +```bash +docker compose up -d --build +``` + +The API listens on `http://localhost:8000`. Browser profiles (`extra/user_data_dir`) and the GeoIP cache live in the +`extra` named volume, so sessions survive container restarts. Details (Xvfb, tini, `/dev/shm`) — +[docs/Deployment.md](docs/Deployment.md). diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e6cf55a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api: + build: . + image: robotex-api + restart: unless-stopped + ports: + - "${API_PORT:-8000}:8000" + env_file: + - path: .env + required: false + volumes: + - extra:/app/extra + # Chrome активно использует /dev/shm, дефолтных 64MB не хватает + shm_size: "2gb" + +volumes: + extra: diff --git a/docs/AntiBot-Bypass-TZ.md b/docs/AntiBot-Bypass-TZ.md index 73f4b16..3d40b29 100644 --- a/docs/AntiBot-Bypass-TZ.md +++ b/docs/AntiBot-Bypass-TZ.md @@ -57,7 +57,7 @@ src/antibot/ ```python from dataclasses import dataclass from enum import Enum -from playwright.async_api import Page +from patchright.async_api import Page class Stage(str, Enum): INTERSTITIAL = "interstitial" @@ -188,7 +188,7 @@ async def solve_interactive(page, info, captcha_key: str): 1. **Траектория мыши** — не прыжок в точку, а несколько промежуточных точек по кривой Безье от текущей позиции курсора до цели, с нелинейным (ease-in-out) распределением скорости и небольшим шумом (±1-2px) на каждом шаге. `page.mouse.move(x, y, steps=N)` даёт линейную интерполяцию — этого мало, нужно звать `mouse.move` несколько раз вручную по расчётным точкам. 2. **Ускорение/замедление клика** — задержка `mouse.down()` -> `mouse.up()` рандомная (100-250мс), плюс случайный сдвиг координаты клика внутри bounding box (не в центр, как это делает и сам Scrapling — `randint(26,28)`). -3. **Общая стелс-часть** (не про капчу, но без неё стадия A вообще не пройдёт): подмена `navigator.webdriver`, canvas noise, консистентные timezone/locale/UA — то, что в вашем проекте уже частично закрыто Camoufox-фингерпринтом в [main.py](../src/main.py) (`generate_context_fingerprint`). Это нужно сохранить как есть — не переизобретать, Camoufox с этим справляется хорошо, вопрос отказа от него у Scrapling был про производительность/стабильность, а не про качество стелса. +3. **Общая стелс-часть** (не про капчу, но без неё стадия A вообще не пройдёт): подмена `navigator.webdriver`, canvas noise, консистентные timezone/locale/UA — сейчас закрывается связкой Patchright + настоящий Google Chrome (`real_chrome=True`) в [engine/stealthy.py](../src/engine/stealthy.py), timezone/locale берутся из геолокации прокси ([api/geoip.py](../src/api/geoip.py)). Camoufox (Firefox) пока остаётся в зависимостях, но как движок не используется — проблемы были скорее с генератором отпечатков, чем с самим Camoufox. ## 6. Оркестратор (`orchestrator.py`) diff --git a/docs/Deployment.md b/docs/Deployment.md new file mode 100644 index 0000000..3247fdd --- /dev/null +++ b/docs/Deployment.md @@ -0,0 +1,62 @@ +# Развёртывание в Docker + +## Быстрый старт + +```bash +docker compose up -d --build +docker compose logs -f api +``` + +API доступно на `http://localhost:8000` (`POST /v1/solve`). + +Без compose: + +```bash +docker build -t robotex-api . +docker run -d --name robotex -p 8000:8000 --shm-size=2g -v robotex-extra:/app/extra robotex-api +``` + +## Что внутри образа + +| Компонент | Зачем | +|---|---| +| `python:3.12-slim-bookworm` | базовый образ, Python той же версии, что и в `.venv` | +| `requirements.txt` | ставится из PyPI (папка `wheels/` в образ не попадает — она под Ubuntu Noble и неполная) | +| Google Chrome stable (`patchright install --with-deps chrome`) | сессия запускается с `real_chrome=True` → channel `chrome` | +| Xvfb (`xvfb-run`) | браузер работает в headful-режиме (`headless=False`), нужен виртуальный дисплей 1920x1080x24 | +| `tini` (ENTRYPOINT, PID 1) | пробрасывает сигналы и подбирает осиротевшие процессы Chrome/Xvfb — без него копятся зомби-процессы | +| пользователь `app` (uid 1000) | Chrome не запускается от root без `--no-sandbox` | + +Рабочая директория — `/app/src`, uvicorn запускается как `uvicorn main:app` (импорты в проекте вида `from api import ...`). + +## Данные и volume + +`/app/extra` вынесен в volume `extra`: + +- `extra/user_data_dir/` — профили браузера (cookies, local storage, fingerprint) по `session_id`; +- `extra/geoip_cache.json` — кэш геолокации прокси. + +Профили со временем накапливаются — чистить вручную: + +```bash +docker compose exec api sh -c 'rm -rf /app/extra/user_data_dir/*' +``` + +## Настройки compose + +- `shm_size: 2gb` — Chrome активно использует `/dev/shm`, дефолтных 64 МБ не хватает (вкладки падают с «Aw, Snap»). +- `API_PORT` — порт на хосте (по умолчанию 8000): `API_PORT=8001 docker compose up -d`. +- `restart: unless-stopped`. +- `.env` подключается, если существует (шаблон — `env.example`). + +## Reverse proxy + +В проде контейнер ставится за Caddy (TLS). Пример `Caddyfile`: + +``` +api.example.com { + reverse_proxy localhost:8000 +} +``` + +Таймауты прокси должны быть не меньше `timeout` запроса (по умолчанию 120 с). diff --git a/docs/Introduction.md b/docs/Introduction.md index dae466c..639d410 100644 --- a/docs/Introduction.md +++ b/docs/Introduction.md @@ -10,7 +10,7 @@ - proxy - обязательно нужно задать прокси, система не станет палить свою инфраструктуру - time_zone - временная зона - locale - языковая настройка -- user_agent - заголовок браузера Firefox, если не передать будет использован свой +- user_agent - User-Agent браузера Chrome, если не передать будет использован свой - screen - размер окна, по умолчанию: 1280х920 - timeout - общий таймаут выполнения запроса, по умолчанию: 120 - captcha - токен сервиса распознавания, если требуется решить капчу на странице diff --git a/requirements.txt b/requirements.txt index eacd032..3031a81 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,7 @@ apify_fingerprint_datapoints==0.15.0 attrs==26.1.0 blessed==1.49.0 browserforge==1.2.4 +camoufox==0.5.6 certifi==2026.7.22 charset-normalizer==3.5.1 click==8.5.0 @@ -47,6 +48,7 @@ numpy==2.5.3 orjson==3.12.0 patchright==1.63.0 platformdirs==4.11.7 +playwright==1.62.0 propcache==0.5.2 psutil==7.2.2 pydantic==2.13.5