You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
chajia/deploy/install-bt-host-mysql.sh

241 lines
7.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env bash
# 宝塔宿主机 MySQL + Docker Redis — 192.168.2.14:30325
# 用法(/home/project/chajia
# bash deploy/install-bt-host-mysql.sh
# 若尚无 .env会自动从 deploy/.env.bt-host-mysql.example 复制
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
COMPOSE_FILE="${ROOT_DIR}/deploy/docker-compose.bt-host-mysql.yml"
ENV_FILE="${ROOT_DIR}/.env"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log() { echo -e "${GREEN}[deploy-bt]${NC} $*"; }
warn() { echo -e "${YELLOW}[deploy-bt]${NC} $*"; }
err() { echo -e "${RED}[deploy-bt]${NC} $*" >&2; }
# 只读取单个变量,避免 source .env 时 RPA_SELECTOR 里的 >> / Pick up 被 bash 当命令执行
read_env() {
local key="$1"
local line
line="$(grep -E "^[[:space:]]*${key}=" "$ENV_FILE" 2>/dev/null | tail -1 || true)"
if [[ -z "$line" ]]; then
echo ""
return
fi
line="${line#*=}"
line="${line#"${line%%[![:space:]]*}"}"
line="${line%"${line##*[![:space:]]}"}"
if [[ "$line" == \"*\" && "$line" == *\" ]]; then
line="${line:1:${#line}-2}"
elif [[ "$line" == \'*\' && "$line" == *\' ]]; then
line="${line:1:${#line}-2}"
fi
echo "$line"
}
validate_env() {
local example="${ROOT_DIR}/deploy/.env.bt-host-mysql.example"
if [[ ! -f "$ENV_FILE" ]]; then
if [[ -f "$example" ]]; then
log "未找到 .env从 deploy/.env.bt-host-mysql.example 自动复制"
cp "$example" "$ENV_FILE"
else
err "未找到 .env 且缺少 ${example}"
exit 1
fi
fi
DATABASE_URL="$(read_env DATABASE_URL)"
JWT_SECRET="$(read_env JWT_SECRET)"
HOST_SERVICE_TOKENS="$(read_env HOST_SERVICE_TOKENS)"
CUSTOMER_REGISTRY="$(read_env CUSTOMER_REGISTRY)"
APP_PORT="$(read_env APP_PORT)"
PUBLIC_HOST="$(read_env PUBLIC_HOST)"
RPA_MOCK_MODE="$(read_env RPA_MOCK_MODE)"
local missing=0
for key in DATABASE_URL JWT_SECRET HOST_SERVICE_TOKENS CUSTOMER_REGISTRY; do
local val="${!key:-}"
if [[ -z "$val" ]] || [[ "$val" == CHANGE_ME* ]]; then
err "请在 .env 中填写: $key"
missing=1
fi
done
if [[ "${RPA_MOCK_MODE:-false}" == "true" ]]; then
warn "RPA_MOCK_MODE=true 仅用于测试,生产请设为 false"
fi
validate_database_credentials
if [[ $missing -ne 0 ]]; then
exit 1
fi
}
validate_database_credentials() {
local pass db_url
pass="$(read_env MYSQL_PASSWORD)"
db_url="$(read_env DATABASE_URL)"
if [[ "$db_url" == *"7WGSADrnDRTh6firE"* ]]; then
err "DATABASE_URL 含已知错误密码7WGSADrn...),宝塔正确密码为 7WGSADm..."
err "请修改 .env 中 MYSQL_PASSWORD 与 DATABASE_URL 后重试"
exit 1
fi
if [[ -n "$pass" && -n "$db_url" && "$db_url" != *":${pass}@"* ]]; then
err "DATABASE_URL 中的密码与 MYSQL_PASSWORD 不一致"
err "请保持两者与宝塔 MySQL 用户 chajia 的密码完全相同"
exit 1
fi
if [[ "$db_url" == *"@127.0.0.1:3307"* ]] || [[ "$db_url" == *"@localhost:3307"* ]]; then
err "DATABASE_URL 指向本机开发库 3307宝塔部署应使用 host.docker.internal:3306"
exit 1
fi
}
preflight_mysql() {
local db_pass
db_pass="$(read_env MYSQL_PASSWORD)"
if [[ -z "$db_pass" ]]; then
return 0
fi
log "测试宝塔 MySQL 连接..."
if docker run --rm --add-host=host.docker.internal:host-gateway mysql:8 \
mysql -h host.docker.internal -u chajia -p"${db_pass}" -e "SELECT 1" chajia >/dev/null 2>&1; then
log "MySQL 连接正常"
return 0
fi
err "MySQL 认证失败P1000请核对 .env 中 MYSQL_PASSWORD 与 DATABASE_URL 是否与宝塔一致"
err "常见原因:重新上传代码覆盖了 .env上传包不再含根目录 .env请手动保留服务器 .env"
err "修复示例:"
err " MYSQL_PASSWORD=宝塔里 chajia 用户的真实密码"
err " DATABASE_URL=mysql://chajia:真实密码@host.docker.internal:3306/chajia"
exit 1
}
deploy_docker() {
if ! command -v docker >/dev/null 2>&1; then
err "未安装 Docker"
exit 1
fi
if ! docker compose version >/dev/null 2>&1; then
err "需要 Docker Compose V2"
exit 1
fi
cd "$ROOT_DIR"
local compose=(docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE")
log "构建并启动(无容器 MySQLRedis 在 Docker 内)..."
"${compose[@]}" up -d --build
log "校验数据库迁移(连接宝塔 MySQL..."
"${compose[@]}" run --rm --no-deps rpa-worker npx prisma migrate deploy
log "同步默认管理员账号admin_demo / kj123456..."
"${compose[@]}" run --rm --no-deps rpa-worker npx prisma db seed
log "容器状态:"
"${compose[@]}" ps
}
smoke_check() {
local port="${APP_PORT:-30325}"
local url="http://127.0.0.1:${port}"
log "健康检查: ${url}"
if curl -sf -o /dev/null -w "%{http_code}" "$url" | grep -qE '200|307|308'; then
log "Web 服务正常"
else
warn "Web 暂未响应请查看docker compose -f deploy/docker-compose.bt-host-mysql.yml logs -f next-app"
fi
preflight_service_token "$port"
}
preflight_service_token() {
local port="${1:-30325}"
local token="${2:-chajia-neibu-2026}"
log "校验 Service Token (${token})..."
local http_code
http_code="$(curl -s -o /dev/null -w "%{http_code}" \
"http://127.0.0.1:${port}/api/quotes/history?page=1&size=1" \
-H "Authorization: Bearer ${token}" \
-H "X-Customer-Id: CUST_001" \
-H "Cache-Control: no-store" 2>/dev/null || echo "000")"
if [[ "$http_code" == "401" ]]; then
err "Service Token 无效:请在 .env 的 HOST_SERVICE_TOKENS 中包含 \"${token}\""
err "示例HOST_SERVICE_TOKENS={\"${token}\":{\"customerId\":\"CUST_001\",\"permissions\":[\"pricing:markup:write\"]}}"
err "修改后执行docker compose --env-file .env -f deploy/docker-compose.bt-host-mysql.yml restart next-app"
exit 1
fi
if [[ "$http_code" =~ ^[245] ]]; then
log "Service Token 校验通过 (HTTP ${http_code})"
else
warn "Service Token 未确认 (HTTP ${http_code}),请手动测试 embed-demo"
fi
preflight_quote_poll_route "$port"
}
preflight_quote_poll_route() {
local port="${1:-30325}"
log "校验 GET /api/quotes/{id} 路由..."
local body http_code
body="$(curl -s \
"http://127.0.0.1:${port}/api/quotes/QTE_smoke_test" \
-H "Authorization: Bearer chajia-neibu-2026" \
-H "X-Customer-Id: CUST_001" \
-H "Accept: application/json" \
-H "Cache-Control: no-store" 2>/dev/null || true)"
if [[ "$body" == \{* ]]; then
log "询价轮询 API 正常(返回 JSON"
return 0
fi
err "GET /api/quotes/{id} 返回 HTML 404embed-demo 会卡在「正在查询报价」"
err "原因:上传包缺少 app/api/quotes/[quote_id]/route.tsWindows 打包方括号路径问题)"
err "请重新打包上传后执行bash deploy/install-bt-host-mysql.sh"
exit 1
}
print_summary() {
local port="${APP_PORT:-30325}"
local host="${PUBLIC_HOST:-<服务器内网IP>}"
echo ""
log "========== 部署完成(宝塔 MySQL 模式)=========="
echo " 内网访问: http://${host}:${port}"
echo " 管理端: http://${host}:${port}/admin/alerts"
echo " 嵌入演示: http://${host}:${port}/embed-demo"
echo " API 基址: http://${host}:${port}"
echo ""
echo " 其它内网系统请求头:"
echo " Authorization: Bearer chajia-neibu-2026"
echo " X-Customer-Id: CUST_001"
echo ""
echo " 常用命令:"
echo " docker compose --env-file .env -f deploy/docker-compose.bt-host-mysql.yml logs -f rpa-worker"
echo " docker compose --env-file .env -f deploy/docker-compose.bt-host-mysql.yml restart"
echo ""
warn "若 embed-demo 长时间「正在查询报价」,多为 RPA Worker 被慢任务阻塞:"
warn " docker compose --env-file .env -f deploy/docker-compose.bt-host-mysql.yml restart rpa-worker"
warn "建议在 .env 设置 RPA_DISABLE_WIDGET_QUOTE_FALLBACK=true模板已默认开启"
}
main() {
validate_env
preflight_mysql
deploy_docker
smoke_check
print_summary
}
main "$@"