# SciLit 生产环境 Docker Compose
#
# 使用方式：
#   cp .env.example .env   # 填入实际值
#   docker compose -f docker-compose.prod.yml --env-file .env up -d
#
# HTTPS 终止建议使用上游反向代理（Caddy / Nginx / ALB），
# 前端 Nginx 容器本身仅提供 HTTP 服务。

version: "3.9"

networks:
  scilit:
    driver: bridge

services:
  postgres:
    image: pgvector/pgvector:pg16
    networks:
      - scilit
    environment:
      POSTGRES_DB: scilit
      POSTGRES_USER: scilit
      POSTGRES_PASSWORD: ${PG_PASSWORD}
    ports: []
    volumes:
      - pgdata:/var/lib/postgresql/data
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U scilit"]
      interval: 10s
      timeout: 5s
      retries: 5
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  redis:
    image: redis:7-alpine
    networks:
      - scilit
    command: redis-server --requirepass ${REDIS_PASSWORD}
    ports: []
    volumes:
      - redisdata:/data
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  elasticsearch:
    image: elasticsearch:8.11.0
    networks:
      - scilit
    environment:
      discovery.type: single-node
      xpack.security.enabled: "false"
      "ES_JAVA_OPTS": "-Xms1g -Xmx1g"
      bootstrap.memory_lock: "true"
    ports: []
    volumes:
      - esdata:/usr/share/elasticsearch/data
    restart: unless-stopped
    ulimits:
      memlock:
        soft: -1
        hard: -1
    healthcheck:
      test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health | grep -qE 'green|yellow'"]
      interval: 15s
      timeout: 10s
      retries: 5
      start_period: 30s
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  minio:
    image: minio/minio:latest
    command: server /data --console-address ":9001"
    networks:
      - scilit
    environment:
      MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
    ports: []
    volumes:
      - miniodata:/data
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://localhost:9000/minio/health/live || exit 1"]
      interval: 15s
      timeout: 5s
      retries: 5
      start_period: 10s
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  migrate:
    build: ./backend
    networks:
      - scilit
    command: alembic -c alembic/alembic.ini upgrade head
    environment:
      DATABASE_URL: postgresql+asyncpg://scilit:${PG_PASSWORD}@postgres:5432/scilit
      JWT_SECRET: ${JWT_SECRET}
      SPECIALTY: ${SPECIALTY:-oncology}
      DEBUG: "false"
    depends_on:
      postgres:
        condition: service_healthy
    restart: "no"

  backend:
    build: ./backend
    networks:
      - scilit
    # ENTRYPOINT 自动执行迁移，CMD 启动 uvicorn（4 workers + proxy-headers）
    # 无需额外 command
    environment:
      DATABASE_URL: postgresql+asyncpg://scilit:${PG_PASSWORD}@postgres:5432/scilit
      REDIS_URL: redis://default:${REDIS_PASSWORD}@redis:6379
      ES_URL: http://elasticsearch:9200
      S3_ENDPOINT: ${S3_ENDPOINT}
      S3_ACCESS_KEY: ${S3_ACCESS_KEY}
      S3_SECRET_KEY: ${S3_SECRET_KEY}
      S3_BUCKET: ${S3_BUCKET:-scilit-files}
      COS_SECRET_ID: ${COS_SECRET_ID:-}
      COS_SECRET_KEY: ${COS_SECRET_KEY:-}
      COS_REGION: ${COS_REGION:-ap-guangzhou}
      COS_BUCKET: ${COS_BUCKET:-scilit-files}
      JWT_SECRET: ${JWT_SECRET}
      SPECIALTY: ${SPECIALTY:-oncology}
      DEBUG: "false"
      SMTP_HOST: ${SMTP_HOST}
      SMTP_PORT: ${SMTP_PORT:-587}
      SMTP_USER: ${SMTP_USER}
      SMTP_PASSWORD: ${SMTP_PASSWORD}
      SMTP_FROM: ${SMTP_FROM}
      SENTRY_DSN: ${SENTRY_DSN}
      AI_API_KEY: ${AI_API_KEY}
      AI_BASE_URL: ${AI_BASE_URL}
      AI_MODEL: ${AI_MODEL}
      PUBMED_API_KEY: ${PUBMED_API_KEY}
      CORS_ORIGINS: ${CORS_ORIGINS}
      PUBLIC_BASE_URL: ${PUBLIC_BASE_URL}
    ports:
      - "127.0.0.1:8000:8000"
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
      elasticsearch:
        condition: service_started
      minio:
        condition: service_healthy
      migrate:
        condition: service_completed_successfully
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://localhost:8000/health || exit 1"]
      interval: 15s
      timeout: 5s
      retries: 3
      start_period: 20s
    restart: unless-stopped
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  worker:
    build: ./backend
    networks:
      - scilit
    # 通过 CMD 覆写默认的 uvicorn 启动，改为 ARQ worker（异步任务消费）
    # 迁移由独立的 migrate 服务完成（depends_on 确保顺序）
    command: arq app.tasks.worker.WorkerSettings
    healthcheck:
      test: ["CMD-SHELL", "grep -q arq /proc/1/cmdline"]
      interval: 30s
      timeout: 5s
      start_period: 30s
      retries: 3
    environment:
      DATABASE_URL: postgresql+asyncpg://scilit:${PG_PASSWORD}@postgres:5432/scilit
      REDIS_URL: redis://default:${REDIS_PASSWORD}@redis:6379
      ES_URL: http://elasticsearch:9200
      S3_ENDPOINT: ${S3_ENDPOINT}
      S3_ACCESS_KEY: ${S3_ACCESS_KEY}
      S3_SECRET_KEY: ${S3_SECRET_KEY}
      S3_BUCKET: ${S3_BUCKET:-scilit-files}
      COS_SECRET_ID: ${COS_SECRET_ID:-}
      COS_SECRET_KEY: ${COS_SECRET_KEY:-}
      COS_REGION: ${COS_REGION:-ap-guangzhou}
      COS_BUCKET: ${COS_BUCKET:-scilit-files}
      JWT_SECRET: ${JWT_SECRET}
      SPECIALTY: ${SPECIALTY:-oncology}
      DEBUG: "false"
      SMTP_HOST: ${SMTP_HOST}
      SMTP_PORT: ${SMTP_PORT:-587}
      SMTP_USER: ${SMTP_USER}
      SMTP_PASSWORD: ${SMTP_PASSWORD}
      SMTP_FROM: ${SMTP_FROM}
      AI_API_KEY: ${AI_API_KEY}
      AI_BASE_URL: ${AI_BASE_URL}
      AI_MODEL: ${AI_MODEL}
      PUBMED_API_KEY: ${PUBMED_API_KEY}
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
      minio:
        condition: service_healthy
      migrate:
        condition: service_completed_successfully
    restart: unless-stopped
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile.prod
    networks:
      - scilit
    environment:
      VITE_API_BASE: /api/v1
    ports:
      - "80:80"
    volumes:
      - /var/log/scilit/nginx:/var/log/nginx
    depends_on:
      backend:
        condition: service_started
    restart: unless-stopped
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

volumes:
  pgdata:
  redisdata:
  esdata:
  miniodata:
