393 lines
14 KiB
Bash
393 lines
14 KiB
Bash
#!/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 "$@" |