gendesign/tradein-mvp/browser/Dockerfile
bot-backend a6afb4ad2b fix(browser): null-guard pageError.location in playwright bundle (#915)
Avito pages emit uncaught JS errors without a location (ad / cross-origin
scripts); playwright 1.60.0's dispatcher reads pageError.location.url with no
null-guard (coreBundle.js ×2: BrowserContextDispatcher + trace event) →
TypeError 'Cannot read properties of undefined (reading url)' crashes the
node-driver protocol connection → 'Connection closed while reading from the
driver' / TargetClosedError → tradein-browser returns 500 and server.py does a
full browser relaunch. Proven on prod run_32: ~6/21 /fetch hit 500, each
forcing a ~5-10s relaunch and stalling the city-sweep.

playwright upgrade is impossible — camoufox 0.4.11 pins the matching
Firefox+juggler build. Patch the pinned bundle in-image:
pageError.location.{url,lineNumber,columnNumber} -> optional chaining (no-op
when location is present). Build asserts >=2 sites patched, failing loudly on a
future playwright bump.

Verified against the deployed bundle: 2 unguarded -> 2 guarded, 0 remaining,
node --check OK.

Refs #883, #901, #916
2026-05-31 21:34:43 +03:00

66 lines
3.7 KiB
Docker
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# tradein-browser — camoufox Playwright WS server (изолированный контейнер Firefox).
# Backend подключается к этому контейнеру через playwright.firefox.connect.
FROM python:3.12-slim
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
# Firefox runtime libs + ca-certificates для TLS
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y --no-install-recommends \
libgtk-3-0 \
libasound2 \
libdbus-glib-1-2 \
libx11-xcb1 \
libxtst6 \
ca-certificates \
&& useradd --create-home --uid 1000 app
WORKDIR /app
# camoufox[geoip] тянет playwright как зависимость
RUN pip install --no-cache-dir "camoufox[geoip]" aiohttp
# ── #915: null-guard для pageError.location в playwright 1.60.0 ─────────────────
# Firefox/juggler-бэкенд иногда отдаёт uncaught JS-ошибку страницы (реклама /
# cross-origin скрипты на avito) БЕЗ поля location. playwright-диспетчер при
# доставке pageError-события читает `pageError.location.url` (coreBundle.js,
# 2 места: BrowserContextDispatcher + trace event) без null-guard →
# `TypeError: Cannot read properties of undefined (reading 'url')` ВНУТРИ
# event-хэндлера node-драйвера → рвёт protocol-соединение → "Connection closed
# while reading from the driver" / TargetClosedError → server.py отдаёт 500 и
# делает полный relaunch браузера (дорого, тормозит sweep). Недетерминированно:
# зависит от того, кинет ли конкретная загрузка locationless-ошибку.
# Апгрейд playwright невозможен — camoufox 0.4.11 pinned под конкретный
# Firefox+juggler. Чиним optional-chaining'ом прямо в pinned-бандле:
# `pageError.location.X` → `pageError.location?.X` (no-op когда location есть;
# отдаёт {url:undefined,...} когда нет → событие доставляется без краша).
# Сборка падает (exit 1) если паттерн не найден — сигнал что bump playwright
# сломал патч.
RUN PW_BUNDLE="$(python -c 'import os,playwright;print(os.path.join(os.path.dirname(playwright.__file__),"driver","package","lib","coreBundle.js"))')" \
&& sed -i -E 's/pageError\.location\.(url|lineNumber|columnNumber)/pageError.location?.\1/g' "$PW_BUNDLE" \
&& N="$(grep -cF 'pageError.location?.url' "$PW_BUNDLE")" \
&& echo "tradein-browser: patched pageError.location?.url sites: $N" \
&& [ "$N" -ge 2 ]
COPY server.py ./server.py
# camoufox fetch качает (а) Firefox-сборку и (б) GeoLite2 mmdb (geoip=True).
# Firefox идёт в $HOME/.cache → ENV HOME=/home/app кладёт его в кэш app-юзера,
# куда рантайм его ищет (фикс #899). НО mmdb пишется в site-packages пакета
# camoufox (root-owned после pip install) — под app это PermissionError на
# GeoLite2-City.mmdb. Поэтому fetch выполняем под ROOT (пишет и mmdb, и —
# благодаря ENV HOME — Firefox в /home/app/.cache), затем chown'им Firefox-кэш
# на app. mmdb остаётся root-owned в site-packages (world-readable → app читает
# на рантайме). Verified реальной сборкой образа 2026-05-31.
ENV HOME=/home/app
RUN python -m camoufox fetch && chown -R app:app /home/app/.cache
USER app
EXPOSE 3000
CMD ["python", "server.py"]