Files
qihuaiflow/run.sh
T

337 lines
14 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
#
# Qihu-AIFlow 智能自动化PaaS平台 - 一键启动脚本
# 支持 Docker Compose 环境和本地开发环境
#
set -e
# ============================================================
# 项目路径检测(兼容 Docker 和本地环境)
# ============================================================
if [ -d "/app" ]; then
PROJECT_DIR="/app"
else
PROJECT_DIR="/data/share/project"
fi
cd "$PROJECT_DIR"
# >>> OUTBOUND PROXY START >>>
export HTTP_PROXY=http://172.18.0.1:7890
export HTTPS_PROXY=http://172.18.0.1:7890
export NO_PROXY=localhost,127.0.0.1,::1,210.16.161.218,172.17.0.0/16,172.18.0.0/16,172.19.0.0/16,172.20.0.0/16,10.0.0.0/8,192.168.0.0/16,.svc,.local,casdoor,qihuaiflow-executor,qihu-aiflow-mysql,qihu-aiflow-redis,qihu-aiflow-pgvector,qihu-aiflow-casdoor,qihuyun.com,ai.qihuyun.com,docs.qihuyun.com,bot.qihuyun.com,siliconflow.cn,api.siliconflow.cn,deepseek.com,api.deepseek.com,volcengineapi.com,aliyun.com,aliyuncs.com,dysmsapi.aliyuncs.com,tencentcloudapi.com,baidubce.com,dashscope.com,qwen.ai,zhipuai.cn,moonshot.cn,minimax.chat,baichuan.com,chatglm.cn,xiaomimimo.com,token-plan-cn.xiaomimimo.com,mi.com,pypi.tuna.tsinghua.edu.cn,feishu.cn,open.feishu.cn,dingtalk.com,open.dingtalk.com,weixin.qq.com,developers.weixin.qq.com,qyapi.weixin.qq.com,ilinkai.weixin.qq.com,tencent.com
# <<< OUTBOUND PROXY END <<<
# Python 后端目录(提前定义,供后续步骤使用)
PYTHON_DIR="$PROJECT_DIR/backend/python"
echo "========================================"
echo " Qihu-AIFlow 智能自动化PaaS平台"
echo " 版本: $(cat $PROJECT_DIR/VERSION 2>/dev/null || echo '1.0.0')"
echo " 时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================"
# ============================================================
# Step 1: 加载配置
# ============================================================
echo "[1/5] 初始化配置..."
# Docker 环境:直接用 docker-compose.yml 传入的环境变量,不加载 .env
# 本地环境:加载 .env 兜底
if [ -f /.dockerenv ] || [ -f /run/.containerenv ]; then
echo " - Docker 环境,使用容器环境变量"
else
echo " - 本地环境"
if [ -f "$PROJECT_DIR/.env" ]; then
set -a
source "$PROJECT_DIR/.env"
set +a
echo " - 已加载 .env 配置文件"
else
echo " - 未找到 .env 文件,使用默认值"
fi
fi
# 仅使用环境变量或默认值,不做.env与env的相互覆盖
MYSQL_HOST="${MYSQL_HOST:-localhost}"
MYSQL_PORT="${MYSQL_PORT:-3306}"
MYSQL_USER="${MYSQL_USER:-root}"
MYSQL_PASSWORD="${MYSQL_PASSWORD:-pwd2026}"
MYSQL_DATABASE="${MYSQL_DATABASE:-aiflow}"
REDIS_HOST="${REDIS_HOST:-localhost}"
REDIS_PORT="${REDIS_PORT:-6379}"
REDIS_PASSWORD="${REDIS_PASSWORD:-pwd2026}"
echo " MySQL : $MYSQL_HOST:$MYSQL_PORT/$MYSQL_DATABASE"
echo " Redis : $REDIS_HOST:$REDIS_PORT"
# ============================================================
# Step 1.5: Docker 环境额外等待(等待 MySQL 端口真正 ready)
# ============================================================
if [ -f /.dockerenv ] || [ -f /run/.containerenv ]; then
echo " - Docker 环境:等待 network/port 完全就绪 (10s)..."
sleep 10
echo " - 等待完成"
fi
# 自动检测 MySQL 客户端 SSL 参数兼容性
# Docker环境(含MariaDB)使用 --ssl=0, 本地MySQL 8.x使用 --ssl-mode=DISABLED
if [ -f /.dockerenv ] || [ -f /run/.containerenv ]; then
MYSQL_SSL_FLAG="--ssl=0"
else
if mysql --ssl=0 -e "SELECT 1" &>/dev/null 2>&1; then
MYSQL_SSL_FLAG="--ssl=0"
else
MYSQL_SSL_FLAG="--ssl-mode=DISABLED"
fi
fi
echo " MySQL SSL 参数: $MYSQL_SSL_FLAG"
# ============================================================
# Step 2: 等待 MySQL 就绪
# ============================================================
echo "[2/5] 等待 MySQL 就绪 (${MYSQL_HOST}:${MYSQL_PORT})..."
echo -n " "
MAX_RETRIES=60
RETRY_COUNT=0
while ! mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" $MYSQL_SSL_FLAG -e "SELECT 1" &>/dev/null; do
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -ge $MAX_RETRIES ]; then
echo ""
echo " ================================================"
echo " [错误] MySQL 连接超时"
echo " ================================================"
echo " 目标: ${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}"
echo " 用户: ${MYSQL_USER}"
echo " 已尝试: ${RETRY_COUNT} 次 (共等待约 $((RETRY_COUNT * 2)) 秒)"
echo ""
echo " --- 网络诊断 ---"
echo ""
# Step A1: DNS 解析测试
echo " [1] DNS 解析测试:"
if command -v getent &>/dev/null; then
getent hosts "$MYSQL_HOST" 2>&1 && echo " DNS 解析成功" || echo " DNS 解析失败: $MYSQL_HOST 无法解析"
elif command -v nslookup &>/dev/null; then
nslookup "$MYSQL_HOST" 2>&1 || echo " DNS 解析失败"
else
echo " (无可用的DNS诊断工具)"
fi
echo ""
# Step A2: TCP 端口连通性测试 (nc)
echo " [2] TCP 端口连通性测试 (nc -zv ${MYSQL_HOST} ${MYSQL_PORT}):"
if command -v nc &>/dev/null; then
nc -zv "$MYSQL_HOST" "$MYSQL_PORT" 2>&1 || echo " TCP 端口不可达"
else
echo " (nc 命令不可用,跳过)"
fi
echo ""
# Step A3: TCP 端口连通性测试 (bash /dev/tcp)
echo " [3] TCP 端口连通性测试 (bash /dev/tcp):"
if timeout 3 bash -c "echo >/dev/tcp/${MYSQL_HOST}/${MYSQL_PORT}" 2>/dev/null; then
echo " TCP 端口 ${MYSQL_HOST}:${MYSQL_PORT} 可达"
else
echo " TCP 端口 ${MYSQL_HOST}:${MYSQL_PORT} 不可达"
fi
echo ""
# Step A4: MySQL 客户端连接测试(显示详细错误)
echo " [4] MySQL 客户端连接测试 (详细错误信息):"
mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" $MYSQL_SSL_FLAG -e "SELECT 1" 2>&1 || true
echo ""
echo " --- 诊断结论 ---"
echo " 可能原因:"
echo " 1. MySQL 容器健康检查通过但 3306 端口尚未完全 ready"
echo " 2. Docker 容器间网络不通(检查是否在同一 Docker 网络)"
echo " 3. MySQL 用户名密码错误"
echo " 4. MySQL 服务虽然运行但未监听 3306 端口"
echo ""
echo " 排查命令 (在宿主机执行):"
echo " docker compose ps"
echo " docker compose logs mysql --tail 50"
echo " docker exec qihu-aiflow-mysql mysqladmin ping -h localhost -u root -ppwd2026"
echo " docker inspect qihu-aiflow -f '{{.HostConfig.NetworkMode}}'"
echo " docker network ls"
echo " ================================================"
exit 1
fi
# 每5次或首次输出进度
if [ $((RETRY_COUNT % 5)) -eq 0 ]; then
echo -n "."
fi
sleep 2
done
echo ""
echo " MySQL 已就绪 (尝试 $RETRY_COUNT 次)"
# ============================================================
# Step 3: 数据库初始化(幂等执行)
# ============================================================
echo "[3/5] 初始化数据库表结构..."
mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" $MYSQL_SSL_FLAG \
-e "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" 2>/dev/null
echo " 使用数据库: $MYSQL_DATABASE"
MIGRATION_DIR="$PROJECT_DIR/migrations"
if [ -d "$MIGRATION_DIR" ]; then
for sql_file in $(ls "$MIGRATION_DIR"/*.sql 2>/dev/null | sort); do
filename=$(basename "$sql_file")
echo " - 执行: $filename"
mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" $MYSQL_SSL_FLAG \
"$MYSQL_DATABASE" < "$sql_file" 2>&1 | sed 's/^/ /' || echo " [警告] $filename 部分语句已跳过(可能已存在)"
done
echo " 数据库初始化完成"
# ============================================================
# Step 3.5: 智能修复 admin 密码哈希
# 仅在密码哈希不可用(NULL/空/无效格式)时才重置为默认密码
# 如果用户已经修改过密码(有效bcrypt哈希),则保持不变
# 注意: 此步骤为可选操作,失败不影响服务启动
# ============================================================
echo " - 检查 admin 密码哈希有效性..."
set +e # 临时允许错误
PYTHON_BIN=""
# 查找可用的 Python(优先虚拟环境,其次系统 Python)
if [ -f "$PROJECT_DIR/backend/python/.venv/bin/python3" ]; then
PYTHON_BIN="$PROJECT_DIR/backend/python/.venv/bin/python3"
elif command -v python3 &>/dev/null; then
PYTHON_BIN="python3"
fi
if [ -n "$PYTHON_BIN" ]; then
# 从数据库读取admin当前的密码哈希
CURRENT_HASH=$(mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" $MYSQL_SSL_FLAG \
"$MYSQL_DATABASE" -sN -e "SELECT password_hash FROM users WHERE username = 'admin';" 2>/dev/null)
# 用Python验证哈希是否有效(bcrypt $2b$格式)
NEED_RESET=$($PYTHON_BIN -c "
import sys
hash_val = sys.argv[1] if len(sys.argv) > 1 else ''
if not hash_val:
print('YES')
elif not hash_val.startswith('\$2b\$') and not hash_val.startswith('\$2a\$'):
print('YES')
else:
# 哈希格式合法,不做强制覆盖(用户可能修改过密码)
print('NO')
" "$CURRENT_HASH" 2>/dev/null) || NEED_RESET="YES"
if [ "$NEED_RESET" = "YES" ]; then
ADMIN_HASH_FIX=$($PYTHON_BIN -c "
import bcrypt
valid_hash = bcrypt.hashpw(b'admin123', bcrypt.gensalt()).decode()
print(valid_hash)
" 2>/dev/null) || true
if [ -n "$ADMIN_HASH_FIX" ]; then
mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" $MYSQL_SSL_FLAG \
"$MYSQL_DATABASE" -e "UPDATE users SET password_hash = '$ADMIN_HASH_FIX' WHERE username = 'admin';" 2>/dev/null || true
echo " admin 密码哈希已重置: admin123 (bcrypt) [原因: 旧哈希无效或缺失]"
else
echo " [警告] 无法生成 bcrypt 哈希,跳过 admin 密码修复"
fi
else
echo " admin 密码哈希有效,保持用户当前密码不变"
fi
else
echo " [警告] Python 不可用,跳过 admin 密码检查(不影响正常启动)"
fi
set -e # 恢复错误退出
else
echo " [警告] 迁移脚本目录不存在: $MIGRATION_DIR"
fi
# ============================================================
# Step 4: 等待 Redis 就绪
# ============================================================
echo "[4/5] 等待 Redis 就绪 (${REDIS_HOST}:${REDIS_PORT})..."
echo -n " "
RETRY_COUNT=0
while ! redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASSWORD" --no-auth-warning ping &>/dev/null; do
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -ge 30 ]; then
echo ""
echo " [警告] Redis 未能在 30 次重试内就绪,将无缓存继续启动"
break
fi
if [ $((RETRY_COUNT % 5)) -eq 0 ]; then
echo -n "."
fi
sleep 1
done
if [ $RETRY_COUNT -lt 30 ]; then
echo ""
echo " Redis 已就绪"
fi
# ============================================================
# Step 5: Python 环境与启动
# ============================================================
echo "[5/5] 配置 Python 环境..."
if [ -d "$PYTHON_DIR/.venv" ]; then
echo " - 使用已存在的虚拟环境"
export PATH="$PYTHON_DIR/.venv/bin:$PATH"
elif [ -d "$PYTHON_DIR" ]; then
echo " - 创建虚拟环境..."
python3 -m venv "$PYTHON_DIR/.venv"
export PATH="$PYTHON_DIR/.venv/bin:$PATH"
echo " - 安装 Python 依赖..."
pip install -r "$PYTHON_DIR/requirements.txt" -i https://pypi.tuna.tsinghua.edu.cn/simple -q 2>&1 | sed 's/^/ /'
echo " - 依赖安装完成"
else
echo " [错误] Python 后端目录不存在: $PYTHON_DIR"
exit 1
fi
# 前端资源检查
if [ -d "$PROJECT_DIR/frontend/dist" ]; then
echo " - 前端资源已就绪"
else
echo " [警告] 前端资源目录不存在,可能无法访问页面"
fi
# ============================================================
# Nginx & 后端启动
# ============================================================
# 如果 nginx 可用,则启动(Docker 环境)
if command -v nginx &>/dev/null; then
echo ""
echo " Nginx 配置测试..."
if nginx -t 2>&1; then
echo " 启动 Nginx (端口 8080)..."
nginx
if [ $? -eq 0 ]; then
echo " Nginx 已启动"
else
echo " [警告] Nginx 启动失败,前端页面可能无法访问"
fi
else
echo " [警告] Nginx 配置有误,跳过启动"
fi
fi
# 创建知识库文件存储目录
mkdir -p "$PROJECT_DIR/data/knowledge"
echo " 知识库存储目录已就绪: $PROJECT_DIR/data/knowledge"
# 启动后端服务(前台运行 - 容器必须保持前台进程)
echo ""
echo "========================================"
echo " Qihu-AIFlow 启动完成"
echo " 前端地址: http://localhost:12888"
echo " 后端地址: http://localhost:18080"
echo " 默认账号: admin / admin123"
echo "========================================"
echo ""
# 防崩溃自检:确认 uvicorn 可导入,缺失则在 venv 内重装(避免依赖缺失导致崩溃循环)
if ! python -c "import uvicorn" >/dev/null 2>&1; then
echo " [自愈] 检测到依赖缺失,重新安装 Python 依赖..."
pip install -r "$PYTHON_DIR/requirements.txt" -i https://pypi.tuna.tsinghua.edu.cn/simple -q 2>&1 | sed 's/^/ /' || echo " [警告] 依赖重装失败"
fi
cd "$PYTHON_DIR"
exec python -m uvicorn main:app --host 0.0.0.0 --port 18080 --log-level info