# Build frontend FROM node:18 as frontend-build WORKDIR /app COPY frontend/package*.json ./ RUN npm install COPY frontend/ ./ RUN npm run build # Build backend FROM python:3.12-slim WORKDIR /app # Create non-root user RUN useradd -m -u 1000 user # Install poetry RUN pip install poetry # Create and configure cache directory RUN mkdir -p /app/.cache && \ chown -R user:user /app # Copy and install backend dependencies COPY backend/pyproject.toml backend/poetry.lock* ./ RUN poetry config virtualenvs.create false \ && poetry install --no-interaction --no-ansi --no-root --only main # Isolated venv for the eval worker (heavy ML deps: torch + braindecode from git), # kept OUT of the web app's pinned stack to avoid version conflicts. Built before # the code COPY so it stays cached across code-only rebuilds. Non-fatal: if the # install fails, the web app still builds and the worker is simply disabled. RUN apt-get update && apt-get install -y --no-install-recommends git \ && python -m venv /opt/worker-venv \ && /opt/worker-venv/bin/pip install --no-cache-dir -U pip \ && (/opt/worker-venv/bin/pip install --no-cache-dir torch --index-url https://download.pytorch.org/whl/cpu \ && /opt/worker-venv/bin/pip install --no-cache-dir huggingface_hub pandas pyarrow open-eeg-bench \ || echo "WARN: eval-worker deps failed to install; worker disabled at runtime") \ && chown -R user:user /opt/worker-venv \ && rm -rf /var/lib/apt/lists/* # Copy backend code COPY backend/ . # Install Node.js and npm RUN apt-get update && apt-get install -y \ curl \ netcat-openbsd \ && curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \ && apt-get install -y nodejs \ && rm -rf /var/lib/apt/lists/* # Copy frontend server and build COPY --from=frontend-build /app/build ./frontend/build COPY --from=frontend-build /app/package*.json ./frontend/ COPY --from=frontend-build /app/server.js ./frontend/ # Install frontend production dependencies WORKDIR /app/frontend RUN npm install --production WORKDIR /app # Environment variables ENV HF_HOME=/app/.cache \ HF_DATASETS_CACHE=/app/.cache \ INTERNAL_API_PORT=7861 \ PORT=7860 \ NODE_ENV=production \ RUN_EVAL_WORKER=1 # Note: HF_TOKEN should be provided at runtime, not build time USER user EXPOSE 7860 # Start the API, the (optional) CPU eval worker, and the frontend. The worker # runs in its own venv as a background --watch loop. Disable it without a rebuild # by setting RUN_EVAL_WORKER=0 in the Space settings, then restarting. CMD ["sh", "-c", "uvicorn app.asgi:app --host 0.0.0.0 --port 7861 & if [ \"$RUN_EVAL_WORKER\" = \"1\" ]; then PYTHONPATH=/app /opt/worker-venv/bin/python -m scripts.run_eval_worker --watch --limit 1 --interval 300 --device cpu & fi; while ! nc -z localhost 7861; do sleep 1; done && cd frontend && npm run serve"]