# ════════════════════════════════════════════════════════════════ # Stage 1: Build the React frontend # ════════════════════════════════════════════════════════════════ FROM node:20-alpine AS frontend-builder WORKDIR /app/frontend # Install dependencies first (better layer caching) COPY frontend/package*.json ./ RUN npm ci --prefer-offline && npm cache clean --force # Build COPY frontend . RUN npm run build # ════════════════════════════════════════════════════════════════ # Stage 2: Build the FastAPI backend (HybridRAG) # ════════════════════════════════════════════════════════════════ FROM python:3.10-slim AS backend WORKDIR /app # ── System dependencies ──────────────────────────────────────── RUN apt-get update && apt-get install -y \ build-essential \ curl \ git \ git-lfs \ && rm -rf /var/lib/apt/lists/* \ && git lfs install --skip-repo # ── Non-root user (security best practice) ──────────────────── RUN addgroup --system appgroup && \ adduser --system --ingroup appgroup --no-create-home appuser # ── CPU-only PyTorch FIRST (saves ~1.8GB vs full CUDA package) ─ RUN pip install --no-cache-dir \ torch==2.2.2+cpu \ --index-url https://download.pytorch.org/whl/cpu # ── Python production dependencies ──────────────────────────── COPY backend/requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # ── Application code ────────────────────────────────────────── COPY backend ./backend # ── Validate data files are real content (not LFS pointers) ─── RUN find /app/backend/data -type f \( -name "*.pdf" -o -name "*.docx" -o -name "*.xlsx" \) | \ while read f; do \ size=$(stat -c%s "$f"); \ if [ "$size" -lt 500 ]; then \ echo "WARNING: $f is an LFS pointer stub ($size bytes) - RAG will be empty!"; \ fi; \ done # ── React frontend static files ─────────────────────────────── RUN mkdir -p ./backend/static COPY --from=frontend-builder /app/frontend/dist ./backend/static # ── Permissions ─────────────────────────────────────────────── # ChromaDB will be downloaded at startup from HF Dataset into data/chroma_db RUN mkdir -p /app/backend/data/chroma_db /app/backend/storage/chroma_db /app/backend/storage/chat_db && \ chown -R appuser:appgroup /app USER appuser # ── Tạo thư mục cache /tmp (writable bởi appuser) ──────────── RUN mkdir -p /tmp/huggingface /tmp/sentence-transformers /tmp/transformers /tmp/.cache && \ chown -R appuser:appgroup /tmp/huggingface /tmp/sentence-transformers /tmp/transformers /tmp/.cache # ── Environment ─────────────────────────────────────────────── ENV PYTHONPATH=/app/backend \ PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ HF_HOME=/tmp/huggingface \ HUGGINGFACE_HUB_CACHE=/tmp/huggingface/hub \ SENTENCE_TRANSFORMERS_HOME=/tmp/sentence-transformers \ TRANSFORMERS_CACHE=/tmp/transformers \ XDG_CACHE_HOME=/tmp/.cache # ── HuggingFace Spaces port ─────────────────────────────────── EXPOSE 7860 # ── Health check ────────────────────────────────────────────── HEALTHCHECK --interval=30s --timeout=15s --start-period=600s --retries=3 \ CMD curl -f http://localhost:7860/api/v1/health || exit 1 # ── Run app ─────────────────────────────────────────────────── CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860", \ "--workers", "1", "--log-level", "info"]