#!/bin/bash # =================================================================== # TopFans Dify 部署脚本 # 功能:服务器上安装 Dify + 配置环境变量 # =================================================================== # # 使用前提: # 1. 服务器已配置 SSH 免密登录 # 2. 服务器有足够的内存(推荐 4GB+) # # 使用方式: # ./dify-deploy.sh install # 安装 Dify 到服务器 # ./dify-deploy.sh status # 查看 Dify 服务状态 # ./dify-deploy.sh logs # 查看 Dify 日志 # ./dify-deploy.sh restart # 重启 Dify # ./dify-deploy.sh upgrade # 升级 Dify 版本 # ./dify-deploy.sh uninstall # 卸载 Dify # ./dify-deploy.sh show-config # 显示需要配置到 TopFans 的环境变量 # # =================================================================== set -e # ==================== 颜色定义 ==================== RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' MAGENTA='\033[0;35m' NC='\033[0m' # ==================== 路径配置 ==================== SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" # ==================== 服务器配置 ==================== SERVER_HOST="101.132.250.62" SERVER_PORT="22" SERVER_USER="root" SERVER_PATH="/opt/dify/docker" # ==================== Dify 配置 ==================== # 端口说明: # EXPOSE_NGINX_PORT: 对外统一入口(nginx),TopFans DIFY_API_BASE 走这个 # CONSOLE_WEB_PORT: Dify 管理控制台(浏览器手动访问) # APP_WEB_PORT: Dify API 容器端口(5001),内部用,外部不直接连 # 默认值对齐 docker/.env.prod 中 DIFY_API_BASE=http://101.132.250.62:8083/v1 DIFY_NGINX_PORT="8083" # Dify Web/API 统一入口(nginx) DIFY_CONSOLE_PORT="8084" # Dify 控制台 DIFY_API_PORT="8085" # Dify API 容器直连(内部) # 密码与密钥:留空,install 时由 openssl rand 生成强随机值 # (避免硬编码弱密码,所有 Dify 实例使用不同密钥) DIFY_DB_PASSWORD="" DIFY_REDIS_PASSWORD="" # ==================== SSH 命令 ==================== ssh_cmd() { ssh -o StrictHostKeyChecking=no -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "$@" } # ==================== docker-compose 命令检测 ==================== # 兼容 v1 二进制与 v2 插件(v2 在新版 Docker 默认安装,但 v1 命令不存在) # 优先用 v1;没有则检查 v2 plugin 并建软链伪装成 v1(参考 deploy.sh:341) dc_cmd() { if ssh_cmd "command -v docker-compose" &>/dev/null; then echo "docker-compose" return 0 fi if ssh_cmd "docker compose version" &>/dev/null; then # v2 插件存在,建软链伪装成 v1 ssh_cmd "ln -sf /usr/libexec/docker/cli-plugins/docker-compose /usr/local/bin/docker-compose 2>/dev/null" || true fi if ! ssh_cmd "command -v docker-compose" &>/dev/null; then print_msg "$RED" "❌ docker-compose 未安装且 v2 插件建软链失败" return 1 fi echo "docker-compose" } # ==================== 打印函数 ==================== print_step() { echo "" echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${BLUE} $1${NC}" echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" } print_msg() { local color=$1 local msg=$2 echo -e "${color}${msg}${NC}" } # ==================== 帮助信息 ==================== show_help() { cat << EOF ${MAGENTA}TopFans Dify 部署脚本${NC} ${YELLOW}命令:${NC} ${GREEN}install${NC} 安装 Dify 到服务器(首次部署) ${GREEN}status${NC} 查看 Dify 服务状态 ${GREEN}logs${NC} 查看 Dify 日志 ${GREEN}restart${NC} 重启 Dify ${GREEN}upgrade${NC} 升级 Dify 版本 ${GREEN}uninstall${NC} 卸载 Dify ${GREEN}show-config${NC} 显示需要配置到 TopFans 的环境变量 ${YELLOW}前提准备:${NC} 1. 服务器已配置 SSH 密钥登录 2. 服务器内存 >= 4GB ${YELLOW}注意事项:${NC} - Dify 使用独立端口(不与 TopFans 冲突) - Dify 使用独立的 PostgreSQL 和 Redis - Workflow 需要在 Dify 控制台手动导入 EOF } # ==================== 1. 安装 Dify ==================== do_install() { print_step "🚀 安装 Dify 到服务器" print_msg "$YELLOW" "目标服务器: ${SERVER_USER}@${SERVER_HOST}:${SERVER_PORT}" print_msg "$YELLOW" "安装路径: ${SERVER_PATH}" echo "" # 检查服务器 Docker 环境 print_msg "$YELLOW" "检查 Docker 环境..." local docker_version=$(ssh_cmd "docker --version 2>/dev/null || echo 'NOT_INSTALLED'") print_msg "$GREEN" "Docker: ${docker_version}" # 如果 Docker 未安装,先安装 if [ "$docker_version" = "NOT_INSTALLED" ]; then print_msg "$YELLOW" "Docker 未安装,开始安装..." ssh_cmd "curl -fsSL https://get.docker.com | sh" ssh_cmd "systemctl start docker && systemctl enable docker" print_msg "$GREEN" "✅ Docker 安装完成" fi # 检查 docker-compose local compose_version=$(ssh_cmd "docker-compose --version 2>/dev/null || docker compose version 2>/dev/null || echo 'NOT_INSTALLED'") print_msg "$GREEN" "docker-compose: ${compose_version}" # 创建服务器目录 print_msg "$YELLOW" "创建目录..." ssh_cmd "mkdir -p ${SERVER_PATH}" print_msg "$GREEN" "✅ 目录创建完成" # 检测并设置 docker-compose 命令(v1/v2 兼容) local DC DC=$(dc_cmd) || { print_msg "$RED" "❌ docker-compose 不可用,请先安装 Docker"; return 1; } # 保护已存在的 .env:重跑 install 时保留用户配置(SECRET_KEY/管理员密码等) if ssh_cmd "[ -f ${SERVER_PATH}/.env ]"; then print_msg "$YELLOW" "⚠️ 检测到已存在的 .env,跳过下载和配置" print_msg "$YELLOW" " 如需重新配置,请先备份 ${SERVER_PATH}/.env 后删除" else # 下载 Dify docker-compose # ★ 版本说明:v0.15.0 的 sandbox 镜像有 seccomp bug(Python 启动崩溃) # 必须用 Dify v1.x 或更新版本 print_msg "$YELLOW" "下载 Dify docker-compose(v1.x)..." ssh_cmd "cd ${SERVER_PATH} && \ curl -L https://raw.githubusercontent.com/langgenius/dify/main/docker/docker-compose.yaml \ -o docker-compose.yml 2>/dev/null || \ curl -L https://raw.githubusercontent.com/langgenius/dify/1.4.0/docker/docker-compose.yaml \ -o docker-compose.yml" print_msg "$GREEN" "✅ docker-compose.yml 下载完成(v1.x)" # 下载 .env 示例文件 print_msg "$YELLOW" "下载 .env 配置(v1.x)..." ssh_cmd "cd ${SERVER_PATH} && \ curl -L https://raw.githubusercontent.com/langgenius/dify/main/docker/.env.example \ -o .env 2>/dev/null || \ curl -L https://raw.githubusercontent.com/langgenius/dify/1.4.0/docker/.env.example \ -o .env" print_msg "$GREEN" "✅ .env 下载完成(v1.x)" # 修改 .env 配置(端口、密码等) # ★ heredoc delimiter 不可加单引号,否则 \$VAR 不展开 print_msg "$YELLOW" "配置 Dify 环境变量..." ssh_cmd "cat > ${SERVER_PATH}/.env.custom << ENVEOF # 端口配置(避免与 TopFans 冲突) EXPOSE_NGINX_PORT=${DIFY_NGINX_PORT} CONSOLE_WEB_PORT=${DIFY_CONSOLE_PORT} APP_WEB_PORT=${DIFY_API_PORT} # 数据库配置 DB_USERNAME=postgres DB_HOST=localhost DB_PORT=5432 DB_DATABASE=dify POSTGRES_PASSWORD=\$(openssl rand -base64 24 | tr -d '/+=' | head -c 32) # Redis 配置 REDIS_PASSWORD=\$(openssl rand -base64 24 | tr -d '/+=' | head -c 32) # API 配置(Dify 官方要求 SECRET_KEY 至少 42 字符) SECRET_KEY=\$(openssl rand -base64 42) INIT_SECRET_KEY=\$(openssl rand -base64 42) ENVEOF " ssh_cmd "cd ${SERVER_PATH} && cat .env .env.custom > .env.tmp && mv .env.tmp .env && rm -f .env.custom" print_msg "$GREEN" "✅ 环境变量配置完成" fi # 启动 Dify print_msg "$YELLOW" "启动 Dify 服务(首次启动需要几分钟)..." ssh_cmd "cd ${SERVER_PATH} && ${DC} up -d" # 等待服务启动 print_msg "$YELLOW" "等待服务启动(最多 120 秒)..." local count=0 while [ $count -lt 120 ]; do local api_status=$(ssh_cmd "curl -s -o /dev/null -w '%{http_code}' http://localhost:${DIFY_API_PORT}/health 2>/dev/null || echo '000'") if [ "$api_status" = "200" ]; then print_msg "$GREEN" "✅ Dify API 就绪" break fi sleep 5 count=$((count + 5)) echo -n "." done echo "" print_step "📊 Dify 安装结果" print_msg "$GREEN" "✅ Dify 安装完成!" echo "" print_msg "$CYAN" "访问地址:" print_msg "$YELLOW" " 控制台: http://${SERVER_HOST}:${DIFY_CONSOLE_PORT}" print_msg "$YELLOW" " Web: http://${SERVER_HOST}:${DIFY_NGINX_PORT}" print_msg "$YELLOW" " API: http://${SERVER_HOST}:${DIFY_API_PORT}" echo "" print_msg "$RED" "⚠️ 首次访问需要创建管理员账号!" echo "" print_msg "$CYAN" "下一步:" print_msg "$YELLOW" " 1. 访问控制台 http://${SERVER_HOST}:${DIFY_CONSOLE_PORT},创建管理员账号" print_msg "$YELLOW" " 2. 在控制台导入 Workflow(docs/dify/*.yml)" print_msg "$YELLOW" " 3. 运行 ./dify-deploy.sh show-config 查看 TopFans 配置" } # ==================== 2. 查看 Dify 状态 ==================== do_status() { print_step "📊 Dify 服务状态" local DC DC=$(dc_cmd) || return 1 ssh_cmd "cd ${SERVER_PATH} && ${DC} ps 2>/dev/null || echo 'Dify 未安装或未启动'" echo "" # 健康检查 local api_status=$(ssh_cmd "curl -s -o /dev/null -w '%{http_code}' http://localhost:${DIFY_API_PORT}/health 2>/dev/null || echo '000'") if [ "$api_status" = "200" ]; then print_msg "$GREEN" "✅ Dify API 健康" else print_msg "$RED" "❌ Dify API 异常 (HTTP ${api_status})" fi } # ==================== 3. 查看日志 ==================== do_logs() { print_step "📋 Dify 日志" print_msg "$YELLOW" "按 Ctrl+C 退出日志" echo "" local DC DC=$(dc_cmd) || return 1 ssh_cmd "cd ${SERVER_PATH} && ${DC} logs -f 2>/dev/null || echo 'Dify 未安装'" } # ==================== 4. 重启 ==================== do_restart() { print_step "🔄 重启 Dify" print_msg "$YELLOW" "正在重启..." local DC DC=$(dc_cmd) || return 1 ssh_cmd "cd ${SERVER_PATH} && ${DC} restart 2>/dev/null" sleep 10 do_status } # ==================== 5. 升级 ==================== do_upgrade() { print_step "⬆️ 升级 Dify" print_msg "$RED" "警告: 升级前建议备份数据!" read -p "确认升级? (y/N): " confirm < /dev/tty if [ "$confirm" != "y" ]; then print_msg "$YELLOW" "已取消" exit 0 fi local DC DC=$(dc_cmd) || return 1 print_msg "$YELLOW" "下载最新 docker-compose..." ssh_cmd "cd ${SERVER_PATH} && \ curl -L https://raw.githubusercontent.com/langgenius/dify/main/docker/docker-compose.yaml \ -o docker-compose.yml" print_msg "$YELLOW" "执行升级..." ssh_cmd "cd ${SERVER_PATH} && ${DC} up -d" print_msg "$GREEN" "✅ 升级完成" } # ==================== 6. 卸载 ==================== do_uninstall() { print_step "🗑️ 卸载 Dify" print_msg "$RED" "警告: 此操作将删除所有 Dify 数据!" read -p "确认卸载? (输入 'YES' 确认): " confirm < /dev/tty if [ "$confirm" != "YES" ]; then print_msg "$YELLOW" "已取消" exit 0 fi local DC DC=$(dc_cmd) || return 1 print_msg "$YELLOW" "停止并删除容器..." ssh_cmd "cd ${SERVER_PATH} && ${DC} down -v 2>/dev/null || true" print_msg "$YELLOW" "删除数据卷..." ssh_cmd "docker volume rm \$(docker volume list -q -f name=dify) 2>/dev/null || true" print_msg "$GREEN" "✅ Dify 卸载完成" } # ==================== 7. 显示 TopFans 配置 ==================== show_topfans_config() { print_step "📝 TopFans 后端配置" print_msg "$CYAN" "需要在 /opt/topfans/docker/.env.prod 中配置以下环境变量:" echo "" print_msg "$YELLOW" "# ========== Dify 配置 ==========" print_msg "$YELLOW" "# Dify API 地址(走 nginx 统一入口 ${DIFY_NGINX_PORT},与 Dify 容器内 APP_WEB_PORT 不同)" print_msg "$CYAN" "DIFY_API_BASE=http://${SERVER_HOST}:${DIFY_NGINX_PORT}/v1" echo "" print_msg "$YELLOW" "# Dify API Key(从 Dify 控制台获取)" print_msg "$CYAN" "DIFY_API_KEY=app-你的APIKey" echo "" print_msg "$RED" "⚠️ 配置完成后需要重启 TopFans 服务:" print_msg "$RED" " ./deploy.sh restart --server ${SERVER_HOST}" } # ==================== 主函数 ==================== main() { if [ $# -eq 0 ]; then show_help exit 0 fi local command=$1 shift case $command in install) do_install ;; status) do_status ;; logs) do_logs ;; restart) do_restart ;; upgrade) do_upgrade ;; uninstall) do_uninstall ;; show-config) show_topfans_config ;; -h|--help|help) show_help ;; *) print_msg "$RED" "错误: 未知命令 '$command'" show_help exit 1 ;; esac } main "$@"