265 lines
13 KiB
Bash
Executable File
265 lines
13 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 仅不拉代码,其余与 pull-and-restart.sh 一致:构建到 deploy/ 并重启
|
||
# 用法:cd 项目根 && ./restart.sh(拉取后可直接执行,无需 chmod)
|
||
# 行尾:LF
|
||
set -e
|
||
ROOT="${PROJECT_ROOT:-$(cd "$(dirname "$0")" && pwd)}"
|
||
cd "$ROOT"
|
||
|
||
run_sudo() { sudo "$@"; }
|
||
|
||
ensure_curl() {
|
||
if command -v curl >/dev/null 2>&1; then return 0; fi
|
||
echo "未检测到 curl,正在安装..."
|
||
if command -v apt-get >/dev/null 2>&1; then run_sudo apt-get update -qq; run_sudo apt-get install -y curl
|
||
elif command -v dnf >/dev/null 2>&1; then run_sudo dnf install -y curl
|
||
elif command -v yum >/dev/null 2>&1; then run_sudo yum install -y curl
|
||
else echo "无法自动安装 curl."; exit 1; fi
|
||
echo "curl 已安装."
|
||
}
|
||
|
||
# ---------- 检测并安装 Docker(用 run_sudo 检测,与后续 compose 一致;支持 Podman)----------
|
||
ensure_docker() {
|
||
if command -v docker >/dev/null 2>&1 && run_sudo docker info >/dev/null 2>&1; then
|
||
echo "Docker 已就绪."
|
||
return 0
|
||
fi
|
||
if command -v docker >/dev/null 2>&1; then
|
||
echo "Docker/Podman 守护进程未连接,尝试启动..."
|
||
run_sudo systemctl start podman 2>/dev/null || true
|
||
run_sudo systemctl start docker 2>/dev/null || true
|
||
if run_sudo docker info >/dev/null 2>&1; then
|
||
echo "Docker 已就绪."
|
||
return 0
|
||
fi
|
||
echo "错误:无法连接 Docker/Podman 守护进程,请执行: sudo systemctl start podman 或 sudo systemctl start docker" >&2
|
||
exit 1
|
||
fi
|
||
echo "未检测到 Docker 或未启动,正在安装..."
|
||
if command -v apt-get >/dev/null 2>&1; then
|
||
run_sudo apt-get update -qq
|
||
run_sudo apt-get install -y docker.io docker-compose-plugin 2>/dev/null || run_sudo apt-get install -y docker.io docker-compose
|
||
run_sudo systemctl start docker
|
||
run_sudo systemctl enable docker
|
||
elif command -v dnf >/dev/null 2>&1 || command -v yum >/dev/null 2>&1; then
|
||
if command -v dnf >/dev/null 2>&1; then
|
||
run_sudo dnf install -y docker
|
||
else
|
||
run_sudo yum install -y docker
|
||
fi
|
||
run_sudo systemctl start docker
|
||
run_sudo systemctl enable docker
|
||
else
|
||
echo "无法自动安装 Docker,请先安装 Docker 与 Docker Compose 后重试."
|
||
exit 1
|
||
fi
|
||
echo "Docker 安装完成."
|
||
}
|
||
|
||
ensure_registry_mirror() {
|
||
REG_CONF_D="/etc/containers/registries.conf.d"
|
||
REG_MIRROR_CONF="$REG_CONF_D/99-docker-mirror.conf"
|
||
echo "配置 Docker Hub 镜像加速(Podman)..."
|
||
run_sudo mkdir -p "$REG_CONF_D"
|
||
run_sudo tee "$REG_MIRROR_CONF" >/dev/null <<'REGEOF'
|
||
# 国内 Docker Hub 拉取加速,多镜像备用
|
||
unqualified-search-registries = ["docker.io"]
|
||
[[registry]]
|
||
location = "docker.io"
|
||
[[registry.mirror]]
|
||
location = "docker.m.daocloud.io"
|
||
[[registry.mirror]]
|
||
location = "docker.1ms.run"
|
||
[[registry.mirror]]
|
||
location = "docker.xuanyuan.me"
|
||
REGEOF
|
||
echo "已写入 $REG_MIRROR_CONF"
|
||
}
|
||
|
||
ensure_docker_compose() {
|
||
run_sudo docker compose version >/dev/null 2>&1 && return 0
|
||
command -v docker-compose >/dev/null 2>&1 && return 0
|
||
[ -x /usr/local/bin/docker-compose ] && return 0
|
||
echo "未检测到 Docker Compose,正在尝试安装(优先插件)..."
|
||
if command -v dnf >/dev/null 2>&1; then
|
||
run_sudo dnf install -y docker-compose-plugin 2>/dev/null || true
|
||
if ! run_sudo docker compose version >/dev/null 2>&1; then
|
||
echo "系统源无插件,尝试添加 Docker CE 源(阿里云镜像)..."
|
||
run_sudo dnf install -y dnf-plugins-core 2>/dev/null || true
|
||
run_sudo dnf config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 2>/dev/null || \
|
||
run_sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 2>/dev/null || true
|
||
run_sudo dnf install -y docker-compose-plugin 2>/dev/null || true
|
||
fi
|
||
elif command -v yum >/dev/null 2>&1; then
|
||
run_sudo yum install -y docker-compose-plugin 2>/dev/null || true
|
||
if ! run_sudo docker compose version >/dev/null 2>&1; then
|
||
echo "系统源无插件,尝试添加 Docker CE 源(阿里云镜像)..."
|
||
run_sudo yum install -y yum-utils 2>/dev/null || true
|
||
run_sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 2>/dev/null || \
|
||
run_sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 2>/dev/null || true
|
||
run_sudo yum install -y docker-compose-plugin 2>/dev/null || true
|
||
fi
|
||
elif command -v apt-get >/dev/null 2>&1; then
|
||
run_sudo apt-get update -qq 2>/dev/null; run_sudo apt-get install -y docker-compose-plugin 2>/dev/null || true
|
||
fi
|
||
run_sudo docker compose version >/dev/null 2>&1 && echo "Docker Compose 插件已就绪." && return 0
|
||
echo "正在安装独立版 Docker Compose(国内 DaoCloud 镜像)..."
|
||
COMPOSE_ARCH="$(uname -m)"
|
||
case "$COMPOSE_ARCH" in
|
||
x86_64) COMPOSE_ARCH=x86_64 ;;
|
||
aarch64|arm64) COMPOSE_ARCH=aarch64 ;;
|
||
*) COMPOSE_ARCH=x86_64 ;;
|
||
esac
|
||
COMPOSE_VER="v2.24.0"
|
||
COMPOSE_URL_CN="https://get.daocloud.io/docker/compose/releases/download/${COMPOSE_VER}/docker-compose-linux-${COMPOSE_ARCH}"
|
||
if ! run_sudo curl -sfL --connect-timeout 20 --max-time 90 "$COMPOSE_URL_CN" -o /usr/local/bin/docker-compose; then
|
||
COMPOSE_URL="https://github.com/docker/compose/releases/download/${COMPOSE_VER}/docker-compose-linux-${COMPOSE_ARCH}"
|
||
run_sudo curl -sfL --max-time 90 "$COMPOSE_URL" -o /usr/local/bin/docker-compose
|
||
fi
|
||
run_sudo chmod +x /usr/local/bin/docker-compose
|
||
run_sudo /usr/local/bin/docker-compose version >/dev/null 2>&1 || { run_sudo rm -f /usr/local/bin/docker-compose; echo "独立版运行失败(可能架构不符),请尝试: dnf install -y docker-compose-plugin 或 yum install -y docker-compose-plugin" >&2; return 0; }
|
||
echo "Docker Compose 已安装."
|
||
}
|
||
|
||
# ---------- 检测并安装 Nginx(反代 + 强制 HTTPS)----------
|
||
ensure_nginx() {
|
||
command -v nginx >/dev/null 2>&1 && return 0
|
||
echo "未检测到 Nginx,正在安装..."
|
||
if command -v apt-get >/dev/null 2>&1; then run_sudo apt-get update -qq; run_sudo apt-get install -y nginx
|
||
elif command -v dnf >/dev/null 2>&1; then run_sudo dnf install -y nginx
|
||
elif command -v yum >/dev/null 2>&1; then run_sudo yum install -y nginx
|
||
else echo "无法自动安装 Nginx."; exit 1; fi
|
||
run_sudo systemctl enable nginx 2>/dev/null || true
|
||
run_sudo systemctl start nginx 2>/dev/null || true
|
||
echo "Nginx 已安装."
|
||
}
|
||
|
||
ensure_curl
|
||
ensure_docker
|
||
ensure_docker_compose
|
||
ensure_registry_mirror
|
||
ensure_nginx
|
||
|
||
resolve_compose_cmd() {
|
||
run_sudo docker compose version >/dev/null 2>&1 && echo "docker compose" && return
|
||
if [ -x /usr/local/bin/docker-compose ]; then
|
||
r=0; run_sudo /usr/local/bin/docker-compose version >/dev/null 2>&1 || r=1
|
||
if [ "$r" -eq 0 ]; then echo "/usr/local/bin/docker-compose"; return; fi
|
||
echo "检测到 /usr/local/bin/docker-compose 无法运行(可能架构不符),正在重装..." >&2
|
||
run_sudo rm -f /usr/local/bin/docker-compose
|
||
ensure_docker_compose || true
|
||
fi
|
||
run_sudo docker-compose version >/dev/null 2>&1 && echo "docker-compose" && return
|
||
ensure_docker_compose || true
|
||
run_sudo docker compose version >/dev/null 2>&1 && echo "docker compose" && return
|
||
if [ -x /usr/local/bin/docker-compose ]; then
|
||
r=0; run_sudo /usr/local/bin/docker-compose version >/dev/null 2>&1 || r=1
|
||
[ "$r" -eq 0 ] && echo "/usr/local/bin/docker-compose" || echo ""
|
||
else
|
||
echo ""
|
||
fi
|
||
}
|
||
COMPOSE_CMD=""
|
||
compose_cmd() {
|
||
if [ -z "$COMPOSE_CMD" ]; then COMPOSE_CMD="$(resolve_compose_cmd)"; fi
|
||
if [ -z "$COMPOSE_CMD" ]; then echo "错误:无法找到 docker compose,请手动安装到 /usr/local/bin/docker-compose"; exit 1; fi
|
||
run_sudo env REGISTRY_MIRROR="${REGISTRY_MIRROR}" GOPROXY="${GOPROXY}" $COMPOSE_CMD "$@"
|
||
}
|
||
|
||
echo "重启 yh_web ($ROOT)(不拉代码,仅构建并启动)..."
|
||
|
||
# 环境配置:缺失时从 server/.env.example 复制
|
||
if [ ! -f server/.env ]; then
|
||
if [ -f server/.env.example ]; then
|
||
cp server/.env.example server/.env
|
||
echo "已从 server/.env.example 创建 server/.env"
|
||
else
|
||
mkdir -p server
|
||
ND="${NGINX_DOMAIN:-yuheng.yuxindazhineng.com}"
|
||
printf 'MONGODB_URI=mongodb://mongo:27017\nMONGODB_DB=yxd-agent-testing\nPORT=8088\nGIN_MODE=release\nALLOWED_ORIGINS=https://%s\n' "$ND" > server/.env
|
||
echo "已创建默认 server/.env"
|
||
fi
|
||
fi
|
||
bash "$ROOT/scripts/merge-server-env-from-example.sh" "$ROOT" || true
|
||
[ -f server/.env ] && sed -i 's/\r$//' server/.env 2>/dev/null || true
|
||
[ -f server/.env ] && set -a && source server/.env && set +a
|
||
|
||
export GOPROXY="${GOPROXY:-https://goproxy.cn,direct}"
|
||
export REGISTRY_MIRROR="${REGISTRY_MIRROR:-docker.m.daocloud.io/library/}"
|
||
|
||
# 与 pull-and-restart 一致:宿主机 9527 检查
|
||
if grep -q '9527' "$ROOT/docker-compose.yml" 2>/dev/null; then
|
||
echo "错误: docker-compose.yml 仍含 9527,会与 sshd 冲突。请拉取最新代码后再执行。" >&2
|
||
exit 1
|
||
fi
|
||
|
||
# 构建到 deploy/(与 pull-and-restart.sh 相同,仅无 git 拉取)
|
||
mkdir -p "$ROOT/deploy/web/dist" "$ROOT/deploy/admin/dist" "$ROOT/deploy/api"
|
||
echo "构建 web 前端 -> deploy/web/dist ..."
|
||
run_sudo docker run --rm -v "$ROOT/web:/app" -v "$ROOT/deploy/web/dist:/out" -w /app \
|
||
"${REGISTRY_MIRROR}node:20-alpine" sh -c "rm -rf /out/* 2>/dev/null; (npm ci --legacy-peer-deps 2>/dev/null || npm install --legacy-peer-deps) && npm run build && cp -r dist/. /out/"
|
||
# 与 pull-and-restart 一致:文档根是 deploy/web/dist,须把 promotion(含 social 视频)拷入 dist
|
||
echo "同步 web/promotion -> deploy/web/dist/promotion ..."
|
||
mkdir -p "$ROOT/deploy/web/dist/promotion"
|
||
if command -v rsync >/dev/null 2>&1; then
|
||
rsync -a --exclude='_pptx_extract/' --exclude='视频发布/' \
|
||
"$ROOT/web/promotion/" "$ROOT/deploy/web/dist/promotion/"
|
||
else
|
||
mkdir -p "$ROOT/deploy/web/dist/promotion/social"
|
||
cp -a "$ROOT/web/promotion/social/." "$ROOT/deploy/web/dist/promotion/social/" 2>/dev/null || true
|
||
[ -f "$ROOT/web/promotion/logo.png" ] && cp -a "$ROOT/web/promotion/logo.png" "$ROOT/deploy/web/dist/promotion/" || true
|
||
[ -f "$ROOT/web/promotion/index.html" ] && cp -a "$ROOT/web/promotion/index.html" "$ROOT/deploy/web/dist/promotion/" || true
|
||
fi
|
||
echo "构建 admin 前端 -> deploy/admin/dist ..."
|
||
# 与 pull-and-restart.sh 一致:须挂载项目根,@yh-web -> ../web/src(仅挂 admin 会构建失败或产物异常)
|
||
run_sudo docker run --rm -v "$ROOT:/repo" -v "$ROOT/deploy/admin/dist:/out" -w /repo/admin \
|
||
"${REGISTRY_MIRROR}node:20-alpine" sh -c "rm -rf /out/* 2>/dev/null; (npm ci --legacy-peer-deps 2>/dev/null || npm install --legacy-peer-deps) && npm run build && cp -r dist/. /out/"
|
||
echo "构建 api 二进制 -> deploy/api/server ..."
|
||
run_sudo docker run --rm -v "$ROOT/server:/src" -v "$ROOT/deploy/api:/out" -w /src -e GOPROXY="${GOPROXY}" \
|
||
"${REGISTRY_MIRROR}golang:1.21-alpine" sh -c "go build -mod=vendor -o /out/server ."
|
||
run_sudo chmod -R a+rX "$ROOT/deploy/web/dist" "$ROOT/deploy/admin/dist" 2>/dev/null || true
|
||
if [ ! -f "$ROOT/deploy/web/dist/index.html" ] || [ ! -f "$ROOT/deploy/admin/dist/index.html" ]; then
|
||
echo "错误: 构建产物不完整(缺少 index.html),请检查上方构建日志。" >&2
|
||
exit 1
|
||
fi
|
||
bash "$ROOT/scripts/verify-admin-dist.sh" "$ROOT"
|
||
|
||
compose_cmd build api
|
||
|
||
MONGO_IMAGE="${REGISTRY_MIRROR}mongo:7"
|
||
if ! run_sudo docker image inspect "$MONGO_IMAGE" >/dev/null 2>&1; then
|
||
echo "拉取 mongo 镜像(仅首次或镜像缺失时)..."
|
||
run_sudo docker pull "$MONGO_IMAGE" || true
|
||
else
|
||
echo "mongo 镜像已存在,跳过拉取."
|
||
fi
|
||
|
||
NGINX_DOMAIN="${NGINX_DOMAIN:-yuheng.yuxindazhineng.com}"
|
||
NGINX_SSL_DIR="/etc/ssl/yh_web/$NGINX_DOMAIN"
|
||
run_sudo mkdir -p "$NGINX_SSL_DIR"
|
||
if [ -f "$ROOT/nginx/$NGINX_DOMAIN.pem" ] && [ -f "$ROOT/nginx/$NGINX_DOMAIN.key" ]; then
|
||
run_sudo cp -f "$ROOT/nginx/$NGINX_DOMAIN.pem" "$NGINX_SSL_DIR/fullchain.pem"
|
||
run_sudo cp -f "$ROOT/nginx/$NGINX_DOMAIN.key" "$NGINX_SSL_DIR/privkey.pem"
|
||
run_sudo chmod 644 "$NGINX_SSL_DIR/fullchain.pem"
|
||
run_sudo chmod 600 "$NGINX_SSL_DIR/privkey.pem"
|
||
elif [ -f "$ROOT/nginx/fullchain.pem" ] && [ -f "$ROOT/nginx/privkey.pem" ]; then
|
||
run_sudo cp -f "$ROOT/nginx/fullchain.pem" "$ROOT/nginx/privkey.pem" "$NGINX_SSL_DIR/"
|
||
run_sudo chmod 644 "$NGINX_SSL_DIR/fullchain.pem"
|
||
run_sudo chmod 600 "$NGINX_SSL_DIR/privkey.pem" 2>/dev/null || true
|
||
elif [ -f "$ROOT/nginx/$NGINX_DOMAIN/fullchain.pem" ] && [ -f "$ROOT/nginx/$NGINX_DOMAIN/privkey.pem" ]; then
|
||
run_sudo cp -f "$ROOT/nginx/$NGINX_DOMAIN/fullchain.pem" "$ROOT/nginx/$NGINX_DOMAIN/privkey.pem" "$NGINX_SSL_DIR/"
|
||
run_sudo chmod 644 "$NGINX_SSL_DIR/fullchain.pem"
|
||
run_sudo chmod 600 "$NGINX_SSL_DIR/privkey.pem" 2>/dev/null || true
|
||
fi
|
||
# shellcheck disable=SC1091
|
||
. "$ROOT/scripts/lib-yh-compose-deploy.sh"
|
||
yh_compose_down
|
||
echo "宿主机 Nginx 站点与服务..."
|
||
yh_install_host_nginx_site_conf
|
||
ensure_host_nginx_started
|
||
yh_compose_up
|
||
|
||
bash "$ROOT/scripts/run-promotion-import-on-deploy.sh" "$ROOT"
|
||
|
||
echo "完成. 对外仅 443,反代: https://$NGINX_DOMAIN"
|