# Single self-contained image: FastAPI backend + built React frontend + # Tesseract OCR. No external services required — pair with the optional # ollama compose profile for local-LLM extraction. FROM node:20-alpine AS frontend WORKDIR /fe COPY frontend/package.json frontend/package-lock.json ./ RUN npm ci --no-audit --no-fund COPY frontend/ ./ RUN npm run build FROM python:3.12-slim RUN apt-get update \ && apt-get install -y --no-install-recommends tesseract-ocr curl \ && rm -rf /var/lib/apt/lists/* WORKDIR /app COPY backend/requirements.txt backend/requirements-ml.txt backend/ RUN pip install --no-cache-dir -r backend/requirements.txt \ && pip install --no-cache-dir torch --index-url https://download.pytorch.org/whl/cpu \ && pip install --no-cache-dir -r backend/requirements-ml.txt COPY backend/ backend/ COPY data/samples/ data/samples/ COPY --from=frontend /fe/dist frontend/dist # Hugging Face Spaces (and other non-root container runtimes) may run the # process as a non-root UID, so make the data dir world-writable for the # SQLite DB + uploaded PDFs. RUN mkdir -p /app/data/uploads && chmod -R 777 /app/data # Bake the zero-shot classifier weights into the image so on-prem/air-gapped # deploys never reach out to huggingface.co at runtime. ENV HF_HOME=/app/.hf RUN cd backend && python -m app.zeroshot --download ENV FRONTEND_DIST=/app/frontend/dist EXPOSE 8000 HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \ CMD curl -fsS http://localhost:8000/api/health || exit 1 WORKDIR /app/backend CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]