# syntax=docker/dockerfile:1.7 # ---- Stage 1: build React SPA ---- FROM node:20-alpine AS web-builder WORKDIR /web # Build-time SPA config — pass via `--build-arg VITE_*=...`. Defaults # point at the DIDI SSO cluster, which is what production uses. ARG VITE_KEYCLOAK_URL=https://sso.clossers.com ARG VITE_KEYCLOAK_REALM=didi-clients ARG VITE_KEYCLOAK_CLIENT_ID=ai-platform-dashboard ARG VITE_KEYCLOAK_REQUIRED_ROLE=admin ARG VITE_STAGING_MODE=false ENV VITE_KEYCLOAK_URL=$VITE_KEYCLOAK_URL \ VITE_KEYCLOAK_REALM=$VITE_KEYCLOAK_REALM \ VITE_KEYCLOAK_CLIENT_ID=$VITE_KEYCLOAK_CLIENT_ID \ VITE_KEYCLOAK_REQUIRED_ROLE=$VITE_KEYCLOAK_REQUIRED_ROLE \ VITE_STAGING_MODE=$VITE_STAGING_MODE # Cache deps COPY web/package.json web/package-lock.json* ./ RUN npm install --no-audit --no-fund # Build COPY web/index.html web/tsconfig.json web/vite.config.ts ./ COPY web/src ./src RUN npm run build # ---- Stage 2: build Python deps ---- FROM python:3.11.12-slim AS builder COPY --from=ghcr.io/astral-sh/uv:0.10 /uv /uvx /bin/ ENV UV_LINK_MODE=copy \ UV_COMPILE_BYTECODE=1 \ UV_PROJECT_ENVIRONMENT=/app/.venv WORKDIR /app COPY pyproject.toml ./ RUN uv venv /app/.venv && uv pip install --python /app/.venv/bin/python \ fastapi uvicorn[standard] pydantic pydantic-settings httpx \ "sqlalchemy[asyncio]>=2.0.36" asyncpg alembic jinja2 python-multipart COPY src ./src RUN uv pip install --python /app/.venv/bin/python -e . # ---- Stage 3: runtime ---- FROM python:3.11.12-slim RUN apt-get update && apt-get install -y --no-install-recommends \ libpq5 \ && rm -rf /var/lib/apt/lists/* COPY --from=builder /app /app COPY --from=web-builder /web/dist /app/web_dist ENV PATH="/app/.venv/bin:$PATH" \ PYTHONPATH=/app/src \ PYTHONUNBUFFERED=1 WORKDIR /app EXPOSE 51300 CMD ["uvicorn", "dashboard.api.app:app", "--host", "0.0.0.0", "--port", "51300"]