# PrimaryDraft: one image, backend serves the built frontend. # # Deliberately does NOT ship the annual-report PDF cache (859 MB). The extracted # records are 620 KB and the pipeline runs entirely from them; PDFs are only # needed to RE-extract, and live ingest downloads what it needs on demand. # ---- stage 1: build the UI ------------------------------------------------- FROM node:22-slim AS ui WORKDIR /ui COPY app/frontend/package*.json ./ RUN npm ci --no-audit --no-fund COPY app/frontend/ ./ RUN npm run build # ---- stage 2: runtime ------------------------------------------------------ FROM python:3.12-slim ENV PYTHONUNBUFFERED=1 PIP_NO_CACHE_DIR=1 PORT=8000 WORKDIR /app COPY app/backend/pyproject.toml ./ COPY app/backend/primarydraft ./primarydraft RUN pip install --no-cache-dir \ "fastapi>=0.115" "uvicorn[standard]>=0.32" "pydantic>=2.9" "httpx>=0.27" \ "pdfplumber>=0.11" "python-docx>=1.1" "python-multipart>=0.0.12" # The BSE scrip master (1.8 MB) so search works on the first request, and # instantly, even if the exchange is slow or blocks the host. # Shipped as seed_cache/ because the Hub refuses paths under .cache/, then # mapped back to the runtime cache location inside the image. COPY app/backend/seed_cache/bse/scrip_master.json ./.cache/bse/scrip_master.json COPY app/backend/seed_cache/mca ./.cache/mca COPY --from=ui /ui/dist ../frontend/dist # Writable for run snapshots, exports and any live ingest. RUN mkdir -p .runs .exports .cache/bse && chmod -R 777 .runs .exports .cache EXPOSE 8000 CMD ["sh","-c","uvicorn primarydraft.main:app --host 0.0.0.0 --port ${PORT:-8000}"]