297 lines
11 KiB
Bash
Executable File
297 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
||
# EasyAI 一键部署脚本
|
||
# 支持交互式问答配置,兼容 IP 与域名两种访问方式
|
||
# 一行命令: git clone https://git.51easyai.com/wangbo/easyai && cd easyai && chmod +x ./start.sh && ./start.sh
|
||
|
||
set -e
|
||
|
||
# 仅配置模式(验证用):DEPLOY_DRY_RUN=1 只生成配置文件,不执行 Docker 安装和启动
|
||
DEPLOY_DRY_RUN="${DEPLOY_DRY_RUN:-0}"
|
||
|
||
# ==================== 项目初始化 ====================
|
||
init_project_dir() {
|
||
local script_source
|
||
script_source="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
||
if [ -f "${script_source}/docker-compose.yml" ]; then
|
||
echo "📁 项目目录: ${script_source}"
|
||
cd "$script_source"
|
||
return 0
|
||
fi
|
||
echo "❌ 未找到 docker-compose.yml,请在 easyai 项目目录下运行 start.sh"
|
||
echo " 启动命令: git clone https://git.51easyai.com/wangbo/easyai && cd easyai && chmod +x ./start.sh && ./start.sh"
|
||
exit 1
|
||
}
|
||
|
||
# ==================== 配置变量(支持环境变量非交互模式) ====================
|
||
DEPLOY_MODE="" # ip | domain
|
||
DEPLOY_IP=""
|
||
DEPLOY_DOMAIN=""
|
||
DEPLOY_HTTPS=false
|
||
|
||
prompt_or_env() {
|
||
local var_name=$1
|
||
local prompt_text=$2
|
||
local env_name=$3
|
||
local default=$4
|
||
|
||
if [ -n "${!env_name}" ]; then
|
||
eval "$var_name=\"${!env_name}\""
|
||
return
|
||
fi
|
||
|
||
if [ -n "$default" ]; then
|
||
read -r -p "${prompt_text} [$default]: " input
|
||
eval "$var_name=\"${input:-$default}\""
|
||
else
|
||
read -r -p "${prompt_text}: " input
|
||
eval "$var_name=\"$input\""
|
||
fi
|
||
}
|
||
|
||
run_deploy_questions() {
|
||
echo ""
|
||
echo "================================"
|
||
echo " EasyAI 部署配置(问答模式)"
|
||
echo "================================"
|
||
echo ""
|
||
|
||
# 非交互模式:环境变量已完整设置则直接使用(CI/自动化部署)
|
||
if [ -n "$DEPLOY_ACCESS" ]; then
|
||
if [ "$DEPLOY_ACCESS" = "ip" ] && [ -n "$DEPLOY_IP" ]; then
|
||
DEPLOY_MODE="ip"
|
||
echo "使用环境变量: IP 模式, IP=$DEPLOY_IP"
|
||
return
|
||
fi
|
||
if [ "$DEPLOY_ACCESS" = "domain" ] && [ -n "$DEPLOY_DOMAIN" ]; then
|
||
DEPLOY_MODE="domain"
|
||
DEPLOY_HTTPS="${DEPLOY_HTTPS_INPUT:-false}"
|
||
echo "使用环境变量: 域名模式, 域名=$DEPLOY_DOMAIN"
|
||
return
|
||
fi
|
||
fi
|
||
|
||
# 1. IP 或域名访问
|
||
if [ -z "$DEPLOY_ACCESS" ]; then
|
||
echo "1. 通过 IP 地址还是域名访问?"
|
||
echo " [1] IP 地址(需开放 3001、3002、3003 端口)"
|
||
echo " [2] 域名"
|
||
read -r -p "请选择 [1/2]: " choice
|
||
case "$choice" in
|
||
1) DEPLOY_MODE="ip" ;;
|
||
2) DEPLOY_MODE="domain" ;;
|
||
*) echo "❌ 无效选择"; exit 1 ;;
|
||
esac
|
||
else
|
||
DEPLOY_MODE="$DEPLOY_ACCESS"
|
||
fi
|
||
|
||
if [ "$DEPLOY_MODE" = "ip" ]; then
|
||
# 2. 输入服务器 IP
|
||
prompt_or_env DEPLOY_IP "2. 请输入服务器 IP 地址" "DEPLOY_IP" ""
|
||
[ -z "$DEPLOY_IP" ] && { echo "❌ IP 不能为空"; exit 1; }
|
||
echo " 请确保防火墙已放行 3001、3002、3003 端口"
|
||
else
|
||
# 3. 输入域名
|
||
prompt_or_env DEPLOY_DOMAIN "3. 请输入域名(不含 https:// 前缀,如 51easyai.com)" "DEPLOY_DOMAIN" ""
|
||
[ -z "$DEPLOY_DOMAIN" ] && { echo "❌ 域名不能为空"; exit 1; }
|
||
|
||
# 3.1 是否启用 HTTPS
|
||
if [ -n "$DEPLOY_HTTPS_INPUT" ]; then
|
||
DEPLOY_HTTPS=$DEPLOY_HTTPS_INPUT
|
||
else
|
||
read -r -p "3.1 是否启用 HTTPS?[y/N]: " https_choice
|
||
DEPLOY_HTTPS=false
|
||
[[ "$https_choice" =~ ^[yY] ]] && DEPLOY_HTTPS=true
|
||
fi
|
||
[ "$DEPLOY_HTTPS" = true ] && echo " 启用 HTTPS 需确保防火墙已放行 80、443 端口"
|
||
fi
|
||
}
|
||
|
||
# ==================== 生成配置文件 ====================
|
||
setup_env_files() {
|
||
echo ""
|
||
echo "📝 配置环境文件..."
|
||
|
||
# 6. 复制 .env.tools 和 .env.ASG
|
||
[ ! -f .env.tools ] && cp .env.tools.sample .env.tools && echo " ✓ .env.tools"
|
||
[ ! -f .env.ASG ] && cp .env.ASG.sample .env.ASG && echo " ✓ .env.ASG"
|
||
|
||
# 4/5. 配置 .env
|
||
if [ ! -f .env ]; then
|
||
cp .env.sample .env
|
||
fi
|
||
|
||
if [ "$DEPLOY_MODE" = "ip" ]; then
|
||
# IP 模式
|
||
sed -i.bak "s|^NUXT_PUBLIC_BASE_APIURL=.*|NUXT_PUBLIC_BASE_APIURL=http://${DEPLOY_IP}:3001|" .env
|
||
sed -i.bak "s|^NUXT_PUBLIC_BASE_SOCKETURL=.*|NUXT_PUBLIC_BASE_SOCKETURL=ws://${DEPLOY_IP}:3002|" .env
|
||
sed -i.bak "s|^NUXT_PUBLIC_SG_APIURL=.*|NUXT_PUBLIC_SG_APIURL=http://${DEPLOY_IP}:3003|" .env
|
||
echo " ✓ .env 已配置为 IP 模式 (${DEPLOY_IP})"
|
||
else
|
||
# 域名模式
|
||
sed -i.bak "s|^NUXT_PUBLIC_BASE_APIURL=.*|NUXT_PUBLIC_BASE_APIURL=/api|" .env
|
||
sed -i.bak "s|^NUXT_PUBLIC_BASE_SOCKETURL=.*|NUXT_PUBLIC_BASE_SOCKETURL=wss://${DEPLOY_DOMAIN}/socket.io|" .env
|
||
sed -i.bak "s|^NUXT_PUBLIC_SG_APIURL=.*|NUXT_PUBLIC_SG_APIURL=/asg-api|" .env
|
||
echo " ✓ .env 已配置为域名模式 (${DEPLOY_DOMAIN})"
|
||
|
||
# 7. Nginx 配置(域名模式)
|
||
PROXY_CONF="${DEPLOY_DOMAIN}.conf"
|
||
if [ ! -f "$PROXY_CONF" ]; then
|
||
sed "s/51easyai.com/${DEPLOY_DOMAIN}/g" easyai-proxy.conf.sample > "$PROXY_CONF"
|
||
echo " ✓ Nginx 配置已生成: $PROXY_CONF"
|
||
fi
|
||
fi
|
||
|
||
rm -f .env.bak
|
||
}
|
||
|
||
# ==================== Docker 安装(复用原 start.sh 逻辑) ====================
|
||
install_docker() {
|
||
echo ""
|
||
echo "================================"
|
||
echo " Docker 安装与检查"
|
||
echo "================================"
|
||
|
||
if [ -f /etc/os-release ]; then
|
||
. /etc/os-release
|
||
case "${ID:-}" in
|
||
ubuntu|debian) OS_FAMILY="Ubuntu" ;;
|
||
centos|rhel|fedora) OS_FAMILY="CentOS" ;;
|
||
*) OS_FAMILY="${ID:-Unknown}" ;;
|
||
esac
|
||
else
|
||
OS_FAMILY=$(hostnamectl 2>/dev/null | grep "Operating System" | awk '{print $3}' || echo "Unknown")
|
||
fi
|
||
OS_VERSION_ID=$(grep -oP '(?<=^VERSION_ID=")[0-9.]+' /etc/os-release 2>/dev/null | cut -d'.' -f1 || echo "0")
|
||
OS_CODENAME=""
|
||
[[ "$OS_FAMILY" == "Ubuntu" ]] && OS_CODENAME=$(lsb_release -cs 2>/dev/null || (grep VERSION_CODENAME /etc/os-release 2>/dev/null | cut -d= -f2 | tr -d '"'))
|
||
|
||
UBUNTU_DOCKER_MIRROR_URL="https://mirrors.ustc.edu.cn/docker-ce"
|
||
UBUNTU_DOCKER_GPG_URL="https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg"
|
||
CENTOS_DOCKER_MIRROR_URL="https://mirrors.tuna.tsinghua.edu.cn/docker-ce"
|
||
|
||
check_network() {
|
||
if curl -sSf --connect-timeout 5 https://download.docker.com/ &>/dev/null; then
|
||
echo "✅ 网络连接正常"
|
||
return 0
|
||
fi
|
||
echo "⚠️ 将使用国内镜像源"
|
||
return 0
|
||
}
|
||
check_network
|
||
|
||
if command -v docker &>/dev/null; then
|
||
echo "✅ Docker 已安装"
|
||
else
|
||
if [[ "$OS_FAMILY" == "Ubuntu" ]]; then
|
||
sudo apt update -y
|
||
sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release
|
||
sudo install -m 0755 -d /etc/apt/keyrings
|
||
curl -fsSL "$UBUNTU_DOCKER_GPG_URL" | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg 2>/dev/null || \
|
||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||
sudo chmod a+r /etc/apt/keyrings/docker.gpg
|
||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] $UBUNTU_DOCKER_MIRROR_URL/linux/ubuntu $OS_CODENAME stable" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
|
||
sudo apt-get update -y
|
||
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||
elif [[ "$OS_FAMILY" == "CentOS" ]]; then
|
||
sudo yum install -y yum-utils
|
||
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
|
||
sudo sed -i "s+https://download.docker.com+$CENTOS_DOCKER_MIRROR_URL+g" /etc/yum.repos.d/docker-ce.repo
|
||
[[ "$OS_VERSION_ID" -ge "8" ]] && sudo yum module disable -y container-tools 2>/dev/null || true
|
||
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||
else
|
||
echo "❌ 不支持的操作系统: $OS_FAMILY"
|
||
exit 1
|
||
fi
|
||
sudo systemctl enable docker
|
||
sudo systemctl start docker
|
||
getent group docker | grep -qw "$USER" || sudo usermod -aG docker "$USER"
|
||
fi
|
||
|
||
if docker compose version &>/dev/null; then
|
||
echo "✅ Docker Compose 已就绪"
|
||
elif command -v docker-compose &>/dev/null; then
|
||
echo "✅ Docker Compose (兼容模式) 已就绪"
|
||
else
|
||
echo "❌ 请安装 docker-compose-plugin"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# ==================== 启动服务 ====================
|
||
start_services() {
|
||
echo ""
|
||
echo "🚀 启动 EasyAI 服务..."
|
||
if docker compose version &>/dev/null; then
|
||
sudo docker compose pull && sudo docker compose up -d
|
||
else
|
||
sudo docker-compose pull && sudo docker-compose up -d
|
||
fi
|
||
echo "🎉 EasyAI 应用启动成功"
|
||
}
|
||
|
||
# ==================== 执行 HTTPS 配置 ====================
|
||
run_https_setup() {
|
||
if [ "$DEPLOY_HTTPS" = true ] && [ -n "$DEPLOY_DOMAIN" ]; then
|
||
echo ""
|
||
echo "🔒 执行 HTTPS 配置(请确保服务器已放行 80、443 端口)..."
|
||
if [ -f "./https.sh" ]; then
|
||
# https.sh 依赖 easyai-proxy.conf,需使用生成的域名配置文件
|
||
export EASYAI_PROXY_CONF="${DEPLOY_DOMAIN}.conf"
|
||
bash ./https.sh
|
||
else
|
||
echo "⚠️ 未找到 https.sh,请手动配置 HTTPS"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# ==================== 主流程 ====================
|
||
main() {
|
||
init_project_dir
|
||
|
||
# 检查是否已有 .env 且非强制重新配置
|
||
if [ -f .env ] && [ -z "$DEPLOY_FORCE_RECONFIG" ] && [ -z "$DEPLOY_ACCESS" ]; then
|
||
echo "📁 检测到已有 .env 配置"
|
||
read -r -p "是否重新配置部署方式?[y/N]: " reconfigure
|
||
if [[ ! "$reconfigure" =~ ^[yY] ]]; then
|
||
echo "⏭️ 使用现有配置继续..."
|
||
DEPLOY_MODE="skip"
|
||
fi
|
||
fi
|
||
|
||
if [ "$DEPLOY_MODE" != "skip" ]; then
|
||
run_deploy_questions
|
||
fi
|
||
|
||
if [ "$DEPLOY_MODE" != "skip" ]; then
|
||
setup_env_files
|
||
else
|
||
[ ! -f .env.tools ] && cp .env.tools.sample .env.tools
|
||
[ ! -f .env.ASG ] && cp .env.ASG.sample .env.ASG
|
||
fi
|
||
|
||
if [ "$DEPLOY_DRY_RUN" = "1" ]; then
|
||
echo ""
|
||
echo "⚠️ dry-run 模式:跳过 Docker 安装和服务启动"
|
||
echo " 配置文件已生成,可直接运行 ./start.sh 完成部署"
|
||
else
|
||
install_docker
|
||
start_services
|
||
run_https_setup
|
||
fi
|
||
|
||
echo ""
|
||
echo "================================"
|
||
echo " 部署完成"
|
||
echo "================================"
|
||
if [ "$DEPLOY_MODE" = "ip" ] && [ -n "$DEPLOY_IP" ]; then
|
||
echo "访问地址: http://${DEPLOY_IP}:3010"
|
||
elif [ "$DEPLOY_MODE" = "domain" ] && [ -n "$DEPLOY_DOMAIN" ]; then
|
||
echo "访问地址: http://${DEPLOY_DOMAIN} (配置 Nginx 后)"
|
||
[ "$DEPLOY_HTTPS" = true ] && echo "HTTPS 已启用"
|
||
fi
|
||
echo ""
|
||
}
|
||
|
||
main "$@"
|