feat(deploy): generic deploy.sh with API-based tenant migration
- Rename deploy-szyx.sh → deploy.sh, remove all customer-specific branding - Migrate mode: prompt for pmk_ key, call public platform export API, pipe to private import API — no MySQL credentials needed - Remove bcrypt dependency (no longer used in script logic) - Update install.sh and verify.sh references Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
父节点
43a423b85c
当前提交
a6a81b0755
@ -194,4 +194,4 @@ printf '\n%b 依赖安装完毕,即将进入交互式部署向导 ...%b\n\n'
|
||||
|
||||
export DEPLOY_HOST
|
||||
cd "$INSTALL_DIR"
|
||||
exec bash scripts/deploy-szyx.sh
|
||||
exec bash scripts/deploy.sh
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
# deploy-szyx.sh — 数字医信私有化一键部署脚本
|
||||
# deploy.sh — XuqmGroup 私有化一键部署脚本
|
||||
#
|
||||
# 用途:在目标机器上完成以下全部步骤:
|
||||
# 0. 交互式选择租户初始化方式(新建 / 迁移)并收集必要信息
|
||||
# 1. 预检(Docker、Compose、python3-bcrypt、磁盘、端口)
|
||||
# 1. 预检(Docker、Compose、磁盘、端口)
|
||||
# 2. 写入配置(.env / secrets.env / xuqm.env / bootstrap.env / nginx)
|
||||
# 3. 登录 ACR 镜像仓库
|
||||
# 4. 启动基础设施容器(MySQL、Redis)并等待就绪
|
||||
# 5. 拉取镜像并启动所有业务容器(base + im + push + update + license)
|
||||
# 6. Schema 扩展(is_default / deletable 列 + 删除保护触发器)+ 系统应用初始化
|
||||
# 7. 租户初始化(新建:验证 bootstrap 结果;迁移:执行 migrate-tenant.sh)
|
||||
# 7. 租户初始化(新建:填写信息并验证;迁移:凭迁移密钥调用 API 导入)
|
||||
# 8. 运行一键验证脚本确认所有服务正常
|
||||
#
|
||||
# 幂等性:可重复执行。已运行的容器不会被重建。
|
||||
@ -17,8 +17,7 @@
|
||||
#
|
||||
# 前提:
|
||||
# - Docker 和 Docker Compose v2 已安装
|
||||
# - python3 和 python3-bcrypt 已安装(用于密码哈希和认证)
|
||||
# - 迁移模式还需要 mysql 客户端
|
||||
# - python3 和 curl 已安装
|
||||
# - 在 XuqmGroup-PrivateDeploy 仓库根目录下执行本脚本
|
||||
#
|
||||
# 覆盖默认值(可通过环境变量传入):
|
||||
@ -30,11 +29,9 @@
|
||||
set -euo pipefail
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 常量 — 数字医信专属配置
|
||||
# 常量
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
CUSTOMER_NAME="数字医信"
|
||||
CUSTOMER_SHORT="szyx"
|
||||
|
||||
# 镜像仓库
|
||||
REGISTRY="crpi-n44qjpuucgjt8e8c.cn-beijing.personal.cr.aliyuncs.com/xuqmgroup"
|
||||
@ -57,12 +54,8 @@ MYSQL_USERNAME="xuqm"
|
||||
# Redis(managed 模式)
|
||||
REDIS_PASSWORD="${REDIS_PASSWORD:-XuqmRedis@2026}"
|
||||
|
||||
# 源生产 MySQL(迁移模式的默认值,交互时可覆盖)
|
||||
SRC_HOST="39.107.53.187"
|
||||
SRC_PORT="3306"
|
||||
SRC_USER="xuqm"
|
||||
SRC_PASSWORD="Xuqm@2026"
|
||||
SRC_DB="xuqm_tenant"
|
||||
# 公有化平台地址(迁移时调用 export API)
|
||||
PUBLIC_PLATFORM_URL="https://dev.xuqinmin.com"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 内部变量
|
||||
@ -93,7 +86,7 @@ container_running() {
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
printf '\n\033[1;35m══════════════════════════════════════════════════\033[0m\n'
|
||||
printf '\033[1;35m %s 私有化一键部署脚本(全量部署)\033[0m\n' "$CUSTOMER_NAME"
|
||||
printf '\033[1;35m XuqmGroup 私有化一键部署脚本(全量部署)\033[0m\n'
|
||||
printf '\033[1;35m══════════════════════════════════════════════════\033[0m\n'
|
||||
printf ' 部署目录: %s\n' "$ROOT_DIR"
|
||||
printf ' 目标主机: %s\n' "$DEPLOY_HOST"
|
||||
@ -106,15 +99,15 @@ printf ' 服务集: base + im + push + update + license\n\n'
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
command -v python3 >/dev/null 2>&1 || \
|
||||
fail "python3 未安装(租户密码哈希需要): apt install -y python3 python3-bcrypt"
|
||||
python3 -c "import bcrypt" 2>/dev/null || \
|
||||
fail "python3-bcrypt 未安装: pip3 install bcrypt 或 apt install -y python3-bcrypt"
|
||||
fail "python3 未安装: apt install -y python3"
|
||||
command -v curl >/dev/null 2>&1 || \
|
||||
fail "curl 未安装: apt install -y curl"
|
||||
|
||||
printf '\033[1;33m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m\n'
|
||||
printf '\033[1;33m 租户初始化方式\033[0m\n'
|
||||
printf '\033[1;33m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m\n'
|
||||
printf ' 1) 新建租户 — 填写租户信息,系统将为您创建全新账号\n'
|
||||
printf ' 2) 迁移租户 — 从生产 MySQL 导入现有租户数据(需主账号认证)\n\n'
|
||||
printf ' 2) 迁移租户 — 从公有平台导入现有租户数据(需迁移密钥)\n\n'
|
||||
|
||||
DEPLOY_MODE=""
|
||||
while [ -z "$DEPLOY_MODE" ]; do
|
||||
@ -165,49 +158,26 @@ if [ "$DEPLOY_MODE" = "new" ]; then
|
||||
|
||||
else
|
||||
# ── 迁移模式:先收集 SRC 配置 + 认证,再继续后续步骤 ──
|
||||
printf '\n ── 迁移源 MySQL 配置(回车使用默认值)──\n'
|
||||
read -rp " 源 MySQL 主机 [${SRC_HOST}]: " _inp; [ -n "$_inp" ] && SRC_HOST="$_inp"
|
||||
read -rp " 源 MySQL 端口 [${SRC_PORT}]: " _inp; [ -n "$_inp" ] && SRC_PORT="$_inp"
|
||||
read -rp " 源 MySQL 用户 [${SRC_USER}]: " _inp; [ -n "$_inp" ] && SRC_USER="$_inp"
|
||||
read -rsp " 源 MySQL 密码 [(隐藏,回车保留默认)]: " _inp; printf '\n'; [ -n "$_inp" ] && SRC_PASSWORD="$_inp"
|
||||
read -rp " 源数据库名 [${SRC_DB}]: " _inp; [ -n "$_inp" ] && SRC_DB="$_inp"
|
||||
# ── 迁移模式:获取迁移密钥 ──
|
||||
printf '\n\033[1;33m 请先访问租户平台安全中心生成迁移密钥:\033[0m\n'
|
||||
printf ' 地址: %s\n' "$PUBLIC_PLATFORM_URL"
|
||||
printf ' 路径: 安全中心 → 私有化部署迁移 → 生成迁移密钥\n'
|
||||
printf ' (密钥仅在生成时显示一次,请及时复制后回到此处)\n\n'
|
||||
|
||||
printf '\n ── 租户主账号认证 ──\n'
|
||||
while [ -z "$DEPLOY_TENANT_EMAIL" ]; do
|
||||
read -rp " 租户主账号邮箱: " DEPLOY_TENANT_EMAIL
|
||||
printf '%s' "$DEPLOY_TENANT_EMAIL" | grep -qE '^[^@]+@[^@]+\.[^@]+$' || {
|
||||
warn "邮箱格式不正确,请重新输入"
|
||||
DEPLOY_TENANT_EMAIL=""
|
||||
_MIGRATE_KEY=""
|
||||
while [ -z "$_MIGRATE_KEY" ]; do
|
||||
read -rsp " 请粘贴迁移密钥: " _MIGRATE_KEY; printf '\n'
|
||||
[[ "$_MIGRATE_KEY" == pmk_* ]] || {
|
||||
warn "密钥格式不正确(应以 pmk_ 开头),请重新输入"
|
||||
_MIGRATE_KEY=""
|
||||
}
|
||||
done
|
||||
ok "迁移密钥已接收"
|
||||
|
||||
read -rsp " 租户主账号密码(仅用于认证,不存储明文): " _MIGRATE_PASSWORD; printf '\n'
|
||||
[ -n "$_MIGRATE_PASSWORD" ] || fail "密码不能为空"
|
||||
|
||||
command -v mysql >/dev/null 2>&1 || \
|
||||
fail "迁移模式需要 mysql 客户端: apt install -y mysql-client"
|
||||
|
||||
printf ' 正在连接生产 MySQL 进行身份认证 ...\n'
|
||||
_SRC_HASH=$(MYSQL_PWD="$SRC_PASSWORD" mysql \
|
||||
-h "$SRC_HOST" -P "$SRC_PORT" -u "$SRC_USER" \
|
||||
--connect-timeout=10 "$SRC_DB" \
|
||||
-N -e "SELECT password FROM t_tenant WHERE email='${DEPLOY_TENANT_EMAIL}' AND type='MAIN' LIMIT 1" 2>/dev/null || true)
|
||||
[ -n "$_SRC_HASH" ] || \
|
||||
fail "生产 MySQL 中未找到租户 ${DEPLOY_TENANT_EMAIL},或无法连接到 ${SRC_HOST}:${SRC_PORT}"
|
||||
|
||||
_AUTH_OK=$(MIGRATE_PW="$_MIGRATE_PASSWORD" MIGRATE_HASH="$_SRC_HASH" python3 -c "
|
||||
import bcrypt, os
|
||||
pw = os.environ['MIGRATE_PW'].encode('utf-8')
|
||||
h = os.environ['MIGRATE_HASH'].encode('utf-8')
|
||||
print('ok' if bcrypt.checkpw(pw, h) else 'fail')
|
||||
")
|
||||
unset _MIGRATE_PASSWORD _SRC_HASH
|
||||
[ "$_AUTH_OK" = "ok" ] || fail "密码认证失败,请确认主账号密码后重试"
|
||||
ok "生产租户 ${DEPLOY_TENANT_EMAIL} 身份认证通过"
|
||||
|
||||
DEPLOY_TENANT_USERNAME="$DEPLOY_TENANT_EMAIL"
|
||||
DEPLOY_TENANT_EMAIL="migrate_placeholder@private.local"
|
||||
DEPLOY_TENANT_USERNAME="migrate_placeholder"
|
||||
DEPLOY_TENANT_NICKNAME=""
|
||||
_BOOTSTRAP_PASSWORD="change-me-on-first-login"
|
||||
_BOOTSTRAP_PASSWORD="already-migrated-do-not-use"
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@ -239,12 +209,12 @@ done
|
||||
# ---------------------------------------------------------------------------
|
||||
# Step 2 — 写入配置
|
||||
# ---------------------------------------------------------------------------
|
||||
step "写入数字医信专属配置(全服务)"
|
||||
step "写入配置(全服务)"
|
||||
|
||||
# .env — 主配置文件,包含所有 profile
|
||||
cat > "$ROOT_DIR/.env" <<EOF
|
||||
# =============================================================================
|
||||
# 数字医信私有化部署主配置
|
||||
# XuqmGroup 私有化部署主配置
|
||||
# 部署时间: $(date '+%Y-%m-%d %H:%M:%S')
|
||||
# 部署主机: ${DEPLOY_HOST}
|
||||
# =============================================================================
|
||||
@ -296,7 +266,7 @@ ok ".env 已写入"
|
||||
# config/secrets.env — 敏感信息(权限 600)
|
||||
mkdir -p "$ROOT_DIR/config"
|
||||
cat > "$ROOT_DIR/config/secrets.env" <<EOF
|
||||
# 数字医信私有化部署 — 敏感凭据(请妥善保管,chmod 600)
|
||||
# XuqmGroup 私有化部署 — 敏感凭据(请妥善保管,chmod 600)
|
||||
MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
|
||||
MYSQL_PASSWORD=${MYSQL_PASSWORD}
|
||||
REDIS_PASSWORD=${REDIS_PASSWORD}
|
||||
@ -307,7 +277,7 @@ ok "config/secrets.env 已写入 (chmod 600)"
|
||||
# config/xuqm.env — 业务服务容器内配置(全服务开启)
|
||||
cat > "$ROOT_DIR/config/xuqm.env" <<EOF
|
||||
# =============================================================================
|
||||
# 数字医信私有化部署 — 业务配置
|
||||
# XuqmGroup 私有化部署 — 业务配置
|
||||
# =============================================================================
|
||||
|
||||
# 私有化部署模式(必须为 PRIVATE)
|
||||
@ -798,44 +768,67 @@ if [ "$DEPLOY_MODE" = "new" ]; then
|
||||
fi
|
||||
|
||||
else
|
||||
# ── 迁移租户:检查是否有现有数据,必要时警告确认后执行迁移 ──
|
||||
# ── 迁移租户:调用公有平台 export API → 私有平台 import API ──
|
||||
|
||||
# 检查是否有现有租户数据
|
||||
_EXIST_CNT="$(docker exec "$MYSQL_CTR" \
|
||||
mysql -u "$MYSQL_USERNAME" -p"${MYSQL_PASSWORD}" "$MYSQL_DATABASE" \
|
||||
-N -e "SELECT COUNT(*) FROM t_tenant" 2>/dev/null || echo 0)"
|
||||
|
||||
if [ "${_EXIST_CNT:-0}" -ge 1 ]; then
|
||||
printf '\n \033[1;31m⚠ 警告:数据库中已存在租户数据!\033[0m\n'
|
||||
printf ' 迁移操作将 \033[1;31m删除所有现有租户信息\033[0m(账号、应用、功能配置),\n'
|
||||
printf ' 并替换为生产环境租户 \033[1m%s\033[0m 的数据。\n\n' "$DEPLOY_TENANT_EMAIL"
|
||||
printf ' 迁移操作将 \033[1;31m删除所有现有租户信息\033[0m(账号、应用、功能配置)。\n\n'
|
||||
read -rp " 确认删除并继续迁移?请输入 yes: " _confirm
|
||||
[ "$_confirm" = "yes" ] || fail "操作已取消"
|
||||
fi
|
||||
|
||||
printf ' 连通性检查生产 MySQL %s ...\n' "$SRC_HOST"
|
||||
MYSQL_PWD="$SRC_PASSWORD" mysql \
|
||||
-h "$SRC_HOST" -P "$SRC_PORT" -u "$SRC_USER" \
|
||||
--connect-timeout=10 "$SRC_DB" \
|
||||
-e "SELECT 1" >/dev/null 2>&1 \
|
||||
|| fail "无法连接生产 MySQL ${SRC_HOST}:${SRC_PORT},请检查网络或凭据"
|
||||
ok "生产 MySQL 连通"
|
||||
printf ' 正在从公有平台导出租户数据 ...\n'
|
||||
_EXPORT_BODY=$(python3 -c "import json, sys; print(json.dumps({'migrationKey': sys.argv[1]}))" "$_MIGRATE_KEY")
|
||||
_EXPORT_RESP=$(curl -sf --max-time 30 -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$_EXPORT_BODY" \
|
||||
"${PUBLIC_PLATFORM_URL}/api/migrate/export") \
|
||||
|| fail "导出失败:请确认迁移密钥有效且未过期(${PUBLIC_PLATFORM_URL})"
|
||||
|
||||
bash "$ROOT_DIR/scripts/migrate-tenant.sh" \
|
||||
--src-host "$SRC_HOST" \
|
||||
--src-port "$SRC_PORT" \
|
||||
--src-user "$SRC_USER" \
|
||||
--src-password "$SRC_PASSWORD" \
|
||||
--src-db "$SRC_DB" \
|
||||
--tenant "$DEPLOY_TENANT_EMAIL"
|
||||
_EXPORT_DATA=$(printf '%s' "$_EXPORT_RESP" | python3 -c "
|
||||
import json, sys
|
||||
resp = json.load(sys.stdin)
|
||||
code = resp.get('code', 0)
|
||||
if code != 200:
|
||||
sys.exit('导出失败: ' + resp.get('message', 'unknown error'))
|
||||
print(json.dumps(resp['data']))
|
||||
") || fail "解析导出数据失败"
|
||||
ok "租户数据已从公有平台导出"
|
||||
|
||||
# 迁移完成后更新 bootstrap.env,防止重启时重新创建占位租户
|
||||
cat > "$ROOT_DIR/config/tenant/bootstrap.env" <<EOF
|
||||
printf ' 正在导入租户数据到私有部署 ...\n'
|
||||
_IMPORT_RESP=$(printf '%s' "$_EXPORT_DATA" | curl -sf --max-time 30 -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
--data-binary @- \
|
||||
"http://127.0.0.1/api/private/migrate/import") \
|
||||
|| fail "导入失败:私有平台 import API 返回错误"
|
||||
|
||||
# 从导出数据中提取租户信息用于最终汇总
|
||||
_TENANT_INFO=$(printf '%s' "$_EXPORT_DATA" | python3 -c "
|
||||
import json, sys
|
||||
d = json.load(sys.stdin)
|
||||
t = d.get('tenant', {})
|
||||
print(t.get('email', ''), t.get('username', ''))
|
||||
")
|
||||
DEPLOY_TENANT_EMAIL=$(printf '%s' "$_TENANT_INFO" | awk '{print $1}')
|
||||
DEPLOY_TENANT_USERNAME=$(printf '%s' "$_TENANT_INFO" | awk '{print $2}')
|
||||
ok "租户 ${DEPLOY_TENANT_EMAIL} 数据已成功导入"
|
||||
|
||||
# 更新 bootstrap.env,防止重启时重新创建占位租户
|
||||
cat > "$ROOT_DIR/config/tenant/bootstrap.env" <<BSENV
|
||||
TENANT_BOOTSTRAP_EMAIL=${DEPLOY_TENANT_EMAIL}
|
||||
TENANT_BOOTSTRAP_USERNAME=${DEPLOY_TENANT_EMAIL}
|
||||
TENANT_BOOTSTRAP_USERNAME=${DEPLOY_TENANT_USERNAME}
|
||||
TENANT_BOOTSTRAP_PASSWORD=already-migrated-do-not-use
|
||||
TENANT_BOOTSTRAP_APP_KEY=ak_private_default
|
||||
EOF
|
||||
BSENV
|
||||
chmod 600 "$ROOT_DIR/config/tenant/bootstrap.env"
|
||||
ok "bootstrap.env 已更新(迁移后)"
|
||||
|
||||
unset _MIGRATE_KEY _EXPORT_BODY _EXPORT_RESP _EXPORT_DATA _IMPORT_RESP _TENANT_INFO
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@ -857,7 +850,7 @@ fi
|
||||
# 最终汇总
|
||||
# ---------------------------------------------------------------------------
|
||||
printf '\n\033[1;35m══════════════════════════════════════════════════\033[0m\n'
|
||||
printf '\033[1;35m %s 私有化部署完成\033[0m\n' "$CUSTOMER_NAME"
|
||||
printf '\033[1;35m XuqmGroup 私有化部署完成\033[0m\n'
|
||||
printf '\033[1;35m══════════════════════════════════════════════════\033[0m\n'
|
||||
printf '\n \033[1m访问地址:\033[0m %s\n' "${CONSOLE_BASE}"
|
||||
printf ' \033[1m运营后台:\033[0m %s/ops\n' "${CONSOLE_BASE}"
|
||||
@ -3,7 +3,7 @@
|
||||
#
|
||||
# 用途:
|
||||
# 部署完成后,全面验证所有服务是否正常运行,输出通过/失败清单。
|
||||
# 可独立运行,也可作为 deploy-szyx.sh 和 migrate-tenant.sh 的最终步骤。
|
||||
# 可独立运行,也可作为 deploy.sh 的最终步骤。
|
||||
#
|
||||
# 使用方法:
|
||||
# # 从部署目录读取配置自动推断
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户