# Standalone scraper microservice.
# Build context: monorepo root (so we can access pyproject.toml + uv.lock + sibling workspace packages).
# Coolify config: base_directory=/, dockerfile_location=/packages/matrx-scraper/Dockerfile.

FROM python:3.13-slim AS base

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1 \
    UV_LINK_MODE=copy \
    UV_COMPILE_BYTECODE=1 \
    PLAYWRIGHT_BROWSERS_PATH=/ms-playwright

# System deps: Coolify's generated healthcheck uses wget while the image's own
# Docker healthcheck uses curl. Keep both available. Playwright browser deps are
# installed later by `playwright install --with-deps`.
#
# 🚨 tesseract-ocr is NOT optional garnish. The `pytesseract` Python wrapper
# ships in the [ocr] extra and shells out to this BINARY; without it every
# scanned PDF — a large share of case law, regulations and out-of-print books —
# died with `TesseractNotFoundError` in production, reported to users as a bad
# document (acquisition-frontier block hunt, 2026-09-17). `-eng` carries the
# English traineddata; the engine cannot read anything without a language pack.
# `/health/ready` reports `ocr` from a real engine probe (server/app.py), so a
# future image that drops this is visible at the door, not one document at a time.
RUN apt-get update && apt-get install -y --no-install-recommends \
        curl \
        wget \
        ca-certificates \
        tesseract-ocr \
        tesseract-ocr-eng \
    && rm -rf /var/lib/apt/lists/* \
    && tesseract --version

RUN pip install --no-cache-dir uv

WORKDIR /app

# uv sync needs the workspace root pyproject.toml + uv.lock and ALL workspace
# members listed in [tool.uv.workspace] members. We copy the whole packages/
# directory (~10 MB) — partial copies break workspace resolution.
COPY pyproject.toml uv.lock ./
COPY packages/ ./packages/

# Install matrx-scraper plus its [server] extras, using the frozen uv.lock
# from the workspace root. `--package matrx-scraper` focuses sync on this
# workspace member only — the heavy aidream-current root project is NOT
# installed. --no-dev skips dev-only tooling.
RUN uv sync --frozen --no-dev --package matrx-scraper --extra server

# Playwright Chromium + its OS deps. Largest layer; isolated for caching.
RUN uv run --no-sync playwright install --with-deps chromium
RUN chmod -R a+rX /ms-playwright

# Default to 8001 to match the existing Coolify Traefik labels and avoid
# proxy reconfiguration. ServerConfig.from_env() respects PORT.
ENV PORT=8001 \
    HOST=0.0.0.0

EXPOSE 8001

# /health/ready confirms DB pool + cache are wired before traffic flows.
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
    CMD curl -fsS http://localhost:8001/health/ready >/dev/null || exit 1

CMD ["uv", "run", "--no-sync", "python", "-m", "matrx_scraper.server"]


# One persistent-browser worker. Build with: --target browser-worker
# Selkies removed the historical v1.6.2 GitHub release and its portable tarball,
# which made a clean browser-worker rebuild fail with HTTP 404. Preserve the
# exact, already-proven runtime bytes from the immutable production image rather
# than depending on a mutable or disappearing third-party download.
FROM 872515272894.dkr.ecr.us-east-1.amazonaws.com/matrx-browser-worker@sha256:93231a01c616a2b2c1751b2af11a76bccaaebbfd7687a4025621a0be4b39204f AS proven-selkies-runtime

FROM base AS browser-worker

# CB-013 — a site must see an ordinary person's Chrome. Real Google Chrome
# (stable, same major as Playwright's Chromium so profiles move between the
# two) carries the "Google Chrome" client-hint brand and the proprietary codecs
# a real browser has; Playwright's Chromium-for-testing announces "Chromium" on
# Linux, which is a headless-farm tell. The desktop font packs give the font
# fingerprint the breadth of a real desktop instead of a bare container's four
# families. identity.py picks /opt/google/chrome/chrome when it exists.
RUN apt-get update && apt-get install -y --no-install-recommends \
        xvfb x11-utils pulseaudio \
        fonts-liberation fonts-liberation2 fonts-dejavu-core fonts-noto-core \
        fonts-noto-color-emoji fonts-freefont-ttf fonts-croscore fonts-crosextra-carlito \
        fonts-crosextra-caladea fonts-urw-base35 \
    && wget -q -O /tmp/google-chrome-stable.deb \
        https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
    && apt-get install -y --no-install-recommends /tmp/google-chrome-stable.deb \
    && rm -f /tmp/google-chrome-stable.deb \
    && rm -rf /var/lib/apt/lists/* \
    && /opt/google/chrome/chrome --version

RUN groupadd --gid 1000 browser-worker \
    && useradd --uid 1000 --gid 1000 --create-home --home-dir /home/browser-worker browser-worker \
    && command -v runuser >/dev/null

COPY --from=proven-selkies-runtime /opt/selkies-gstreamer /opt/selkies-gstreamer
RUN test -x /opt/selkies-gstreamer/selkies-gstreamer-run

# The identity VALUES the worker presents when the run policy names none
# (matrx_scraper/cloud_browser/worker/identity.py). A US-west clock and locale,
# and a common desktop GPU in place of Xvfb's SwiftShader. Empty WEBGL values
# turn the graphics patch off.
ENV DISPLAY=:99 \
    BROWSER_WORKER_PORT=8002 \
    HOME=/home/browser-worker \
    XDG_RUNTIME_DIR=/tmp/browser-worker-runtime \
    BROWSER_WORKER_CHROME_BINARY=/opt/google/chrome/chrome \
    BROWSER_WORKER_TIMEZONE=America/Los_Angeles \
    BROWSER_WORKER_LOCALE=en-US \
    BROWSER_WORKER_WEBGL_VENDOR="Google Inc. (Intel)" \
    BROWSER_WORKER_WEBGL_RENDERER="ANGLE (Intel, Mesa Intel(R) UHD Graphics 630 (CFL GT2), OpenGL 4.6)" \
    TZ=America/Los_Angeles

COPY packages/matrx-scraper/docker/browser-worker-entrypoint.sh /usr/local/bin/browser-worker-entrypoint
RUN chmod +x /usr/local/bin/browser-worker-entrypoint

# The entrypoint starts as root only long enough to initialize root-owned
# Fargate volume mount points, then immediately execs as browser-worker.
USER root

EXPOSE 8002
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
    CMD curl -fsS http://localhost:8002/health >/dev/null || exit 1
ENTRYPOINT ["/usr/local/bin/browser-worker-entrypoint"]
CMD []


# Coolify's scraper-service application builds this Dockerfile without an
# explicit target. Keep the ordinary scraper image as the final/default stage;
# browser-worker is opt-in via `--target browser-worker` only.
FROM base AS scraper-service
