name: zm-rag

services:
  # ── Infrastructure ──────────────────────────────────────────────────

  opensearch:
    build:
      context: ./opensearch
      dockerfile: Dockerfile
    container_name: zm-rag-opensearch
    environment:
      # JVM heap
      - OPENSEARCH_JAVA_OPTS=-Xms4g -Xmx4g
      # Cluster
      - cluster.name=zm-rag-cluster
      - node.name=zm-rag-node-1
      - discovery.type=single-node
      # Disable security for internal network
      - plugins.security.disabled=true
      # Network
      - network.host=0.0.0.0
      # Enable kNN plugin
      - knn.plugin.enabled=true
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    volumes:
      - opensearch_data:/usr/share/opensearch/data
      - ./opensearch/analysis:/usr/share/opensearch/config/analysis
      - ./opensearch/opensearch.yml:/usr/share/opensearch/config/opensearch.yml:ro
    ports:
      - "9200:9200"
    networks:
      - zm-rag-net
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://localhost:9200/_cluster/health | grep -qE '\"status\":\"(green|yellow)\"' || exit 1"]
      interval: 30s
      timeout: 15s
      retries: 10
      start_period: 60s

  opensearch-dashboards:
    image: opensearchproject/opensearch-dashboards:2.19.4
    container_name: zm-rag-dashboards
    depends_on:
      opensearch:
        condition: service_healthy
    environment:
      - OPENSEARCH_HOSTS=["http://opensearch:9200"]
      - DISABLE_SECURITY_DASHBOARDS_PLUGIN=true
    ports:
      - "5601:5601"
    networks:
      - zm-rag-net
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://localhost:5601/api/status | grep -q 'available' || exit 1"]
      interval: 30s
      timeout: 15s
      retries: 10
      start_period: 120s

  neo4j:
    image: neo4j:5-community
    container_name: zm-rag-neo4j
    environment:
      - NEO4J_AUTH=neo4j/zm_rag_2024
      - NEO4J_PLUGINS=["apoc"]
      - NEO4J_server_memory_heap_initial__size=2g
      - NEO4J_server_memory_heap_max__size=4g
      - NEO4J_server_memory_pagecache_size=2g
      - NEO4J_dbms_security_procedures_unrestricted=apoc.*
      - NEO4J_dbms_security_procedures_allowlist=apoc.*
    volumes:
      - neo4j_data:/data
      - neo4j_logs:/logs
    ports:
      - "8474:7474"
      - "8687:7687"
    networks:
      - zm-rag-net
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "cypher-shell -u neo4j -p zm_rag_2024 'RETURN 1' || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 30s

  redis:
    image: redis:alpine
    container_name: zm-rag-redis
    command: redis-server --maxmemory 4gb --maxmemory-policy allkeys-lru --appendonly yes
    volumes:
      - redis_data:/data
    ports:
      - "16379:6379"
    networks:
      - zm-rag-net
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  mysql:
    image: mysql:8.0
    container_name: zm-rag-mysql
    environment:
      - MYSQL_ROOT_PASSWORD=zm123456
      - MYSQL_DATABASE=zm_rag
      - MYSQL_CHARSET=utf8mb4
      - TZ=Asia/Shanghai
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
      - --default-time-zone=+08:00
      - --max-connections=200
    volumes:
      - mysql_data:/var/lib/mysql
    ports:
      - "13306:3306"
    networks:
      - zm-rag-net
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-pzm123456"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 30s

  # ── Document Converter (Java / Spire.Office) ────────────────────────

  doc-converter:
    build:
      context: ../converter
      dockerfile: Dockerfile
    container_name: zm-rag-doc-converter
    depends_on:
      mysql:
        condition: service_healthy
    environment:
      - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/zm_rag?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
      - SPRING_DATASOURCE_USERNAME=root
      - SPRING_DATASOURCE_PASSWORD=zm123456
    ports:
      - "18800:18800"
    volumes:
      - D:/data/files:/data/files
    networks:
      - zm-rag-net
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://localhost:18800/actuator/health || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 60s

  # ── Celery Worker ───────────────────────────────────────────────────
  # Backend (uvicorn) runs locally for development with --reload.
  # Only the Celery worker is containerised.

  celery-worker:
    build:
      context: ..
      dockerfile: docker/backend/Dockerfile
    container_name: zm-rag-celery-worker
    depends_on:
      opensearch:
        condition: service_healthy
      neo4j:
        condition: service_healthy
      redis:
        condition: service_healthy
      doc-converter:
        condition: service_healthy
    env_file:
      - ./backend/.env.docker
    # API keys injected from host environment or .env
    environment:
      - LLM_BASE_URL=${LLM_BASE_URL:-https://dashscope.aliyuncs.com/compatible-mode/v1}
      - LLM_API_KEY=${LLM_API_KEY:-}
      - LLM_MODEL=${LLM_MODEL:-qwen3.5-flash}
      - LLM_ENABLE_THINKING=${LLM_ENABLE_THINKING:-false}
      - EMBEDDING_BASE_URL=${EMBEDDING_BASE_URL:-https://dashscope.aliyuncs.com/compatible-mode/v1}
      - EMBEDDING_API_KEY=${EMBEDDING_API_KEY:-}
      - EMBEDDING_MODEL=${EMBEDDING_MODEL:-text-embedding-v4}
      # HuggingFace mirror + cache
      - HF_ENDPOINT=https://hf-mirror.com
      - HF_HOME=/root/.cache/huggingface
    volumes:
      # Bind mount — shared with local backend for file access
      - D:/data/files:/data/files
      # HuggingFace model cache — avoid re-downloading on container rebuild
      - D:/data/hf_cache:/root/.cache/huggingface
    networks:
      - zm-rag-net
    restart: unless-stopped

volumes:
  opensearch_data:
    driver: local
  neo4j_data:
    driver: local
  neo4j_logs:
    driver: local
  redis_data:
    driver: local
  mysql_data:
    driver: local

networks:
  zm-rag-net:
    driver: bridge
