初始提交: Qihu-AIFlow v2.0.1 完整项目
- 前端 React+TypeScript (源码+构建产物) - 后端 Python FastAPI - 数据库迁移 001-015 - Docker部署配置 - 微信ClawBot API频道绑定 - 项目文档 README.md
This commit is contained in:
commit
78ff261b5a
199 files changed
+69126
No files matched your search
@@ -0,0 +1,324 @@
|
||||
#!/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"
|
||||
|
||||
# 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 ""
|
||||
|
||||
cd "$PYTHON_DIR"
|
||||
exec python -m uvicorn main:app --host 0.0.0.0 --port 18080 --log-level info
|
||||
Reference in new issue
Block a user