Spaces:
Running
Running
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # ClinicalMatch AI β HuggingFace Spaces Dockerfile | |
| # Single container: Neo4j Community + FastAPI + Next.js + Nginx (supervisord) | |
| # Exposed port: 7860 (HF Spaces default) | |
| # Persistent storage: /data (Neo4j data lives here β survives restarts) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # ββ Stage 1: Build Next.js ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| FROM node:20-slim AS frontend-builder | |
| WORKDIR /build/frontend | |
| COPY frontend/package*.json ./ | |
| RUN npm install --legacy-peer-deps --prefer-offline | |
| COPY frontend/ ./ | |
| # Build with empty API URL so all requests are relative (Nginx routes them) | |
| ENV NEXT_PUBLIC_API_URL="" | |
| RUN npm run build | |
| # ββ Stage 2: Final runtime image ββββββββββββββββββββββββββββββββββββββββββββββ | |
| FROM ubuntu:22.04 | |
| ENV DEBIAN_FRONTEND=noninteractive | |
| ENV LANG=C.UTF-8 | |
| # ββ System dependencies ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| RUN apt-get update && apt-get install -y --no-install-recommends \ | |
| python3.11 python3-pip python3.11-venv \ | |
| nginx supervisor \ | |
| curl wget ca-certificates gnupg \ | |
| && rm -rf /var/lib/apt/lists/* | |
| # ββ Node.js 20 ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ | |
| && apt-get install -y --no-install-recommends nodejs \ | |
| && rm -rf /var/lib/apt/lists/* | |
| # ββ Neo4j 5.x Community + cypher-shell (official Debian repo) βββββββββββββββββ | |
| RUN curl -fsSL https://debian.neo4j.com/neotechnology.gpg.key \ | |
| | gpg --dearmor -o /usr/share/keyrings/neo4j.gpg \ | |
| && echo "deb [signed-by=/usr/share/keyrings/neo4j.gpg] https://debian.neo4j.com stable 5" \ | |
| > /etc/apt/sources.list.d/neo4j.list \ | |
| && apt-get update \ | |
| && apt-get install -y neo4j cypher-shell \ | |
| && rm -rf /var/lib/apt/lists/* | |
| ENV NEO4J_HOME=/var/lib/neo4j | |
| ENV PATH="/usr/bin:${PATH}" | |
| # ββ Neo4j configuration (sed replaces existing keys, avoids duplicate errors) ββ | |
| RUN CFG=/etc/neo4j/neo4j.conf && \ | |
| sed -i 's|^#*\s*server\.bolt\.listen_address=.*|server.bolt.listen_address=0.0.0.0:7687|' $CFG && \ | |
| sed -i 's|^#*\s*server\.http\.listen_address=.*|server.http.listen_address=0.0.0.0:7474|' $CFG && \ | |
| sed -i 's|^#*\s*server\.directories\.data=.*|server.directories.data=/data/neo4j/data|' $CFG && \ | |
| sed -i 's|^#*\s*server\.directories\.logs=.*|server.directories.logs=/data/neo4j/logs|' $CFG && \ | |
| sed -i 's|^#*\s*server\.memory\.heap\.initial_size=.*|server.memory.heap.initial_size=256m|' $CFG && \ | |
| sed -i 's|^#*\s*server\.memory\.heap\.max_size=.*|server.memory.heap.max_size=512m|' $CFG && \ | |
| sed -i 's|^#*\s*server\.memory\.pagecache\.size=.*|server.memory.pagecache.size=128m|' $CFG && \ | |
| { \ | |
| echo "db.transaction.timeout=60s"; \ | |
| echo "dbms.logs.query.enabled=OFF"; \ | |
| echo "dbms.security.procedures.unrestricted=apoc.*"; \ | |
| echo "dbms.security.procedures.allowlist=apoc.*"; \ | |
| } >> $CFG | |
| # ββ Python backend ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| WORKDIR /app/backend | |
| COPY backend/requirements.txt . | |
| RUN pip3 install --no-cache-dir -r requirements.txt | |
| COPY backend/ . | |
| # ββ Next.js frontend (pre-built) βββββββββββββββββββββββββββββββββββββββββββββββ | |
| WORKDIR /app/frontend | |
| # Copy only what Next.js needs to run (not dev deps) | |
| COPY --from=frontend-builder /build/frontend/.next/standalone ./ | |
| COPY --from=frontend-builder /build/frontend/.next/static ./.next/static | |
| COPY --from=frontend-builder /build/frontend/public ./public | |
| # ββ Config files βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| COPY docker/nginx.conf /app/docker/nginx.conf | |
| COPY docker/supervisord.conf /app/docker/supervisord.conf | |
| COPY docker/entrypoint.sh /app/docker/entrypoint.sh | |
| COPY docker/seed_on_startup.sh /app/docker/seed_on_startup.sh | |
| RUN chmod +x /app/docker/entrypoint.sh /app/docker/seed_on_startup.sh | |
| # ββ Nginx writable dirs (runs without root after init) ββββββββββββββββββββββββ | |
| RUN mkdir -p /tmp/nginx-cache /tmp/nginx-body /tmp/nginx-run \ | |
| && chown -R www-data:www-data /var/log/nginx /var/lib/nginx 2>/dev/null || true | |
| # ββ Expose & environment βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| EXPOSE 7860 | |
| # Neo4j β local Community instance (no Aura) | |
| ENV NEO4J_URI=bolt://127.0.0.1:7687 | |
| ENV NEO4J_USERNAME=neo4j | |
| ENV NEO4J_PASSWORD=clinicalmatch2024 | |
| ENV NEO4J_DATABASE=neo4j | |
| # LLM β OpenAI-compatible (set real values via HF Spaces secrets) | |
| ENV OPENAI_API_KEY="" | |
| ENV OPENAI_BASE_URL=https://ai.aimlapi.com/v1 | |
| ENV OPENAI_MODEL=claude-opus-4-7 | |
| # Next.js standalone listens on 3000 internally; Nginx routes externally | |
| ENV PORT=3000 | |
| ENV HOSTNAME=127.0.0.1 | |
| WORKDIR /app | |
| ENTRYPOINT ["/app/docker/entrypoint.sh"] | |