# Dashboard Module - Docker Compose # # Port Allocation (Dev: 51300): # 51300 - Dashboard API + Web UI # 15432 - PostgreSQL (optional host exposure for debugging) # # Naming Convention: didiAI-{module}-{service} networks: didi-network: external: true # single shared network for all DIDI + AI platform stacks volumes: dashboard_db_data: driver: local services: # ========================================================================== # PostgreSQL # ========================================================================== dashboard-db: container_name: didiAI-dashboard-db image: postgres:16-alpine networks: - didi-network environment: - POSTGRES_USER=${DASHBOARD_DB_USER} - POSTGRES_PASSWORD=${DASHBOARD_DB_PASSWORD} - POSTGRES_DB=${DASHBOARD_DB_NAME} volumes: - dashboard_db_data:/var/lib/postgresql/data ports: - "15432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U ${DASHBOARD_DB_USER} -d ${DASHBOARD_DB_NAME}"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped profiles: - dashboard # ========================================================================== # Dashboard API + Web UI # ========================================================================== dashboard-api: container_name: didiAI-dashboard image: didiai-dashboard build: context: .. dockerfile: deploy/Dockerfile args: # SPA build-time config — Keycloak settings get baked into the JS bundle. # Default: local Keycloak proxied through admin nginx at /auth/. VITE_KEYCLOAK_URL: ${VITE_KEYCLOAK_URL:-https://10.11.10.11:3001/auth} VITE_KEYCLOAK_REALM: ${VITE_KEYCLOAK_REALM:-didi-admins} VITE_KEYCLOAK_CLIENT_ID: ${VITE_KEYCLOAK_CLIENT_ID:-ai-platform-dashboard} VITE_KEYCLOAK_REQUIRED_ROLE: ${VITE_KEYCLOAK_REQUIRED_ROLE:-admin} VITE_STAGING_MODE: ${VITE_STAGING_MODE:-false} ports: - "51300:51300" networks: - didi-network depends_on: dashboard-db: condition: service_healthy env_file: - .env environment: - DASHBOARD_HOST=0.0.0.0 - DASHBOARD_PORT=51300 - DASHBOARD_DATABASE_URL=postgresql+asyncpg://${DASHBOARD_DB_USER}:${DASHBOARD_DB_PASSWORD}@didiAI-dashboard-db:5432/${DASHBOARD_DB_NAME} - DASHBOARD_BRAIN_URL=${DASHBOARD_BRAIN_URL:-http://didibrain-api:8090} # Keycloak SSO (leave empty to disable JWT auth) - DASHBOARD_KEYCLOAK_URL=${DASHBOARD_KEYCLOAK_URL:-} - DASHBOARD_KEYCLOAK_REALM=${DASHBOARD_KEYCLOAK_REALM:-didi-clients} - DASHBOARD_KEYCLOAK_CLIENT_ID=${DASHBOARD_KEYCLOAK_CLIENT_ID:-ai-platform-dashboard} - DASHBOARD_KEYCLOAK_REQUIRED_ROLE=${DASHBOARD_KEYCLOAK_REQUIRED_ROLE:-admin} - DASHBOARD_STAGING_MODE=${DASHBOARD_STAGING_MODE:-false} # OTel — traces to Jaeger via OTel Collector - OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_EXPORTER_OTLP_ENDPOINT:-http://didi-otel-collector:4317} - OTEL_SERVICE_NAME=didiAI-dashboard # Module health endpoint overrides — point at real upstreams # (raw vLLM at 10.11.10.15 instead of the unbuilt FastAPI wrapper at 10.11.10.17:14100/14200) - DASHBOARD_EMBEDDINGS_HEALTH_URL=${DASHBOARD_EMBEDDINGS_HEALTH_URL:-http://10.11.10.15:8200/v1/models} - DASHBOARD_RERANK_HEALTH_URL=${DASHBOARD_RERANK_HEALTH_URL:-http://10.11.10.15:8100/v1/models} # Catalog runs locally on Docker DNS; /v1/status is the aggregated reachability probe # (catalog does not expose /v1/info — it consumes it from other modules) - DASHBOARD_CATALOG_HEALTH_URL=${DASHBOARD_CATALOG_HEALTH_URL:-http://didiAI-catalog-api:11000/v1/status} # Gateway is nginx — only /health is auth-free and inline - DASHBOARD_GATEWAY_HEALTH_URL=${DASHBOARD_GATEWAY_HEALTH_URL:-http://didiAI-gateway:11000/health} # Brain doesn't expose /v1/info — point at /health (matches catalog rationale) - DASHBOARD_BRAIN_HEALTH_URL=${DASHBOARD_BRAIN_HEALTH_URL:-http://didibrain-api:8090/health} healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:51300/health')"] interval: 30s timeout: 10s retries: 3 start_period: 30s restart: unless-stopped profiles: - dashboard