| # 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}"] | |