name: dify-prod

x-api-common: &api-common
  image: langgenius/dify-api:${DIFY_VERSION}
  restart: always
  env_file:
    - ./.env
  networks:
    - default
    - ssrf_proxy_network
  volumes:
    - ./volumes/app/storage:/app/api/storage

services:
  # 初始化卷权限，避免 api / worker 启动时写不进 storage
  init_permissions:
    image: busybox:latest
    command:
      - sh
      - -c
      - |
        mkdir -p /app/api/storage
        chown -R 1001:1001 /app/api/storage
    volumes:
      - ./volumes/app/storage:/app/api/storage
    restart: "no"

  api:
    <<: *api-common
    environment:
      MODE: api
      HOME: /tmp
      XDG_RUNTIME_DIR: /tmp
      LANG: C.UTF-8
      LC_ALL: C.UTF-8
      PYTHONIOENCODING: utf-8
      UV_CACHE_DIR: /tmp/.uv-cache
      PLUGIN_REMOTE_INSTALL_HOST: ${EXPOSE_PLUGIN_DEBUGGING_HOST:-localhost}
      PLUGIN_REMOTE_INSTALL_PORT: ${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003}
      PLUGIN_MAX_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800}
      PLUGIN_DAEMON_TIMEOUT: ${PLUGIN_DAEMON_TIMEOUT:-1200}
      INNER_API_KEY_FOR_PLUGIN: ${PLUGIN_DIFY_INNER_API_KEY}

  worker:
    <<: *api-common
    environment:
      MODE: worker
      HOME: /tmp
      XDG_RUNTIME_DIR: /tmp
      LANG: C.UTF-8
      LC_ALL: C.UTF-8
      PYTHONIOENCODING: utf-8
      UV_CACHE_DIR: /tmp/.uv-cache
      PLUGIN_MAX_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800}
      INNER_API_KEY_FOR_PLUGIN: ${PLUGIN_DIFY_INNER_API_KEY}

  worker_beat:
    <<: *api-common
    environment:
      MODE: beat
      HOME: /tmp
      XDG_RUNTIME_DIR: /tmp
      LANG: C.UTF-8
      LC_ALL: C.UTF-8
      PYTHONIOENCODING: utf-8
      UV_CACHE_DIR: /tmp/.uv-cache

  web:
    image: langgenius/dify-web:${DIFY_VERSION}
    restart: always
    env_file:
      - ./.env
    networks:
      - default

  sandbox:
    image: langgenius/dify-sandbox:${DIFY_SANDBOX_VERSION}
    restart: always
    env_file:
      - ./.env
    environment:
      API_KEY: ${SANDBOX_API_KEY}
      GIN_MODE: ${SANDBOX_GIN_MODE:-release}
      WORKER_TIMEOUT: ${SANDBOX_WORKER_TIMEOUT:-15}
      ENABLE_NETWORK: ${SANDBOX_ENABLE_NETWORK:-true}
      HTTP_PROXY: ${SANDBOX_HTTP_PROXY:-http://ssrf_proxy:3128}
      HTTPS_PROXY: ${SANDBOX_HTTPS_PROXY:-http://ssrf_proxy:3128}
      SANDBOX_PORT: ${SANDBOX_PORT:-8194}
      PIP_MIRROR_URL: ${PIP_MIRROR_URL:-}
    volumes:
      - ./volumes/sandbox/dependencies:/dependencies
      - ./volumes/sandbox/conf:/conf
    networks:
      - ssrf_proxy_network

  plugin_daemon:
    image: langgenius/dify-plugin-daemon:${DIFY_PLUGIN_DAEMON_VERSION}
    restart: always
    env_file:
      - ./.env
    environment:
      DB_DATABASE: ${DB_PLUGIN_DATABASE:-dify_plugin}
      DB_SSL_MODE: ${DB_SSL_MODE:-disable}
      SERVER_PORT: ${PLUGIN_DAEMON_PORT:-5002}
      SERVER_KEY: ${PLUGIN_DAEMON_KEY}
      MAX_PLUGIN_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800}
      DIFY_INNER_API_URL: ${PLUGIN_DIFY_INNER_API_URL:-http://api:5001}
      DIFY_INNER_API_KEY: ${PLUGIN_DIFY_INNER_API_KEY}
      PLUGIN_REMOTE_INSTALLING_HOST: ${PLUGIN_DEBUGGING_HOST:-0.0.0.0}
      PLUGIN_REMOTE_INSTALLING_PORT: ${PLUGIN_DEBUGGING_PORT:-5003}
      PLUGIN_WORKING_PATH: ${PLUGIN_WORKING_PATH:-/app/storage/cwd}
      FORCE_VERIFYING_SIGNATURE: ${FORCE_VERIFYING_SIGNATURE:-true}
      PYTHON_ENV_INIT_TIMEOUT: ${PLUGIN_PYTHON_ENV_INIT_TIMEOUT:-600}
      PLUGIN_MAX_EXECUTION_TIMEOUT: ${PLUGIN_MAX_EXECUTION_TIMEOUT:-1200}
      PLUGIN_STDIO_BUFFER_SIZE: ${PLUGIN_STDIO_BUFFER_SIZE:-1024}
      PLUGIN_STDIO_MAX_BUFFER_SIZE: ${PLUGIN_STDIO_MAX_BUFFER_SIZE:-5242880}
      PIP_MIRROR_URL: ${PIP_MIRROR_URL:-https://mirrors.aliyun.com/pypi/simple/}
      PLUGIN_IGNORE_UV_LOCK: ${PLUGIN_IGNORE_UV_LOCK:-true}
      PYTHON_COMPILE_ALL_EXTRA_ARGS: ${PYTHON_COMPILE_ALL_EXTRA_ARGS:--x \.venv}

      PLUGIN_STORAGE_TYPE: ${PLUGIN_STORAGE_TYPE:-aliyun_oss}
      PLUGIN_STORAGE_OSS_BUCKET: ${PLUGIN_STORAGE_OSS_BUCKET}
      PLUGIN_INSTALLED_PATH: ${PLUGIN_INSTALLED_PATH:-plugin}
      PLUGIN_PACKAGE_CACHE_PATH: ${PLUGIN_PACKAGE_CACHE_PATH:-plugin_packages}
      PLUGIN_MEDIA_CACHE_PATH: ${PLUGIN_MEDIA_CACHE_PATH:-assets}

      ALIYUN_OSS_REGION: ${PLUGIN_ALIYUN_OSS_REGION}
      ALIYUN_OSS_ENDPOINT: ${PLUGIN_ALIYUN_OSS_ENDPOINT}
      ALIYUN_OSS_ACCESS_KEY_ID: ${PLUGIN_ALIYUN_OSS_ACCESS_KEY_ID}
      ALIYUN_OSS_ACCESS_KEY_SECRET: ${PLUGIN_ALIYUN_OSS_ACCESS_KEY_SECRET}
      ALIYUN_OSS_AUTH_VERSION: ${PLUGIN_ALIYUN_OSS_AUTH_VERSION:-v4}
      ALIYUN_OSS_PATH: ${PLUGIN_ALIYUN_OSS_PATH:-dify-prod/plugin}
    volumes:
      - ./volumes/plugin_daemon:/app/storage
    networks:
      - default
      - ssrf_proxy_network

  ssrf_proxy:
    image: ubuntu/squid:latest
    restart: always
    volumes:
      - ./ssrf_proxy/squid.conf.template:/etc/squid/squid.conf.template
      - ./ssrf_proxy/docker-entrypoint.sh:/docker-entrypoint-mount.sh
    entrypoint:
      - sh
      - -c
      - |
        cp /docker-entrypoint-mount.sh /docker-entrypoint.sh
        sed -i 's/\r$$//' /docker-entrypoint.sh
        chmod +x /docker-entrypoint.sh
        /docker-entrypoint.sh
    environment:
      HTTP_PORT: ${SSRF_HTTP_PORT:-3128}
      COREDUMP_DIR: /var/spool/squid
      REVERSE_PROXY_PORT: ${SSRF_REVERSE_PROXY_PORT:-8194}
      SANDBOX_HOST: ${SSRF_SANDBOX_HOST:-sandbox}
      SANDBOX_PORT: ${SANDBOX_PORT:-8194}
    networks:
      - default
      - ssrf_proxy_network

  nginx:
    image: nginx:latest
    restart: always
    depends_on:
      - api
      - web
    ports:
      - "${EXPOSE_NGINX_PORT:-80}:${NGINX_PORT:-80}"
      - "${EXPOSE_NGINX_SSL_PORT:-443}:${NGINX_SSL_PORT:-443}"
    volumes:
      - ./nginx/nginx.conf.template:/etc/nginx/nginx.conf.template
      - ./nginx/proxy.conf.template:/etc/nginx/proxy.conf.template
      - ./nginx/https.conf.template:/etc/nginx/https.conf.template
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/docker-entrypoint.sh:/docker-entrypoint-mount.sh
      - ./nginx/ssl:/etc/ssl
      - ./volumes/certbot/conf:/etc/letsencrypt
      - ./volumes/certbot/www:/var/www/html
    entrypoint:
      - sh
      - -c
      - |
        cp /docker-entrypoint-mount.sh /docker-entrypoint.sh
        sed -i 's/\r$$//' /docker-entrypoint.sh
        chmod +x /docker-entrypoint.sh
        /docker-entrypoint.sh
    environment:
      NGINX_SERVER_NAME: ${NGINX_SERVER_NAME:-_}
      NGINX_HTTPS_ENABLED: ${NGINX_HTTPS_ENABLED:-false}
      NGINX_SSL_PORT: ${NGINX_SSL_PORT:-443}
      NGINX_PORT: ${NGINX_PORT:-80}
      NGINX_SOCKET_IO_UPSTREAM: ${NGINX_SOCKET_IO_UPSTREAM:-api:5001}
      NGINX_SSL_CERT_FILENAME: ${NGINX_SSL_CERT_FILENAME:-dify.crt}
      NGINX_SSL_CERT_KEY_FILENAME: ${NGINX_SSL_CERT_KEY_FILENAME:-dify.key}
      NGINX_SSL_PROTOCOLS: ${NGINX_SSL_PROTOCOLS:-TLSv1.2 TLSv1.3}
      NGINX_WORKER_PROCESSES: ${NGINX_WORKER_PROCESSES:-auto}
      NGINX_CLIENT_MAX_BODY_SIZE: ${NGINX_CLIENT_MAX_BODY_SIZE:-100M}
      NGINX_KEEPALIVE_TIMEOUT: ${NGINX_KEEPALIVE_TIMEOUT:-65}
      NGINX_PROXY_READ_TIMEOUT: ${NGINX_PROXY_READ_TIMEOUT:-3600s}
      NGINX_PROXY_SEND_TIMEOUT: ${NGINX_PROXY_SEND_TIMEOUT:-3600s}
      NGINX_ENABLE_CERTBOT_CHALLENGE: ${NGINX_ENABLE_CERTBOT_CHALLENGE:-false}
    networks:
      - default

# 显式指定网段，避免和阿里云 VPC 内网冲突
networks:
  default:
    driver: bridge
    ipam:
      config:
        - subnet: 192.168.240.0/24
          gateway: 192.168.240.1

  ssrf_proxy_network:
    driver: bridge
    internal: true
    ipam:
      config:
        - subnet: 192.168.241.0/24
          gateway: 192.168.241.1
