website-scripts/deploy-ai.sh

474 lines
22 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.

#!/usr/bin/env bash
# 本地修改 → 上传服务器 → 构建/更新 → 部署 (tlyq.ai)
#
# 策略:服务器上直接 npm run build复用容器内已安装的 node_modules
#
# 支持系统:
# - macOS (本地): zsh + bash 兼容模式
# - Ubuntu/Debian (服务器): dash (符号链接到 sh) + bash
# - Rocky Linux/CentOS/RHEL (服务器): bash
#
# 用法bash deploy-ai.sh [选项]
# 无参数 — 自动检测(初次构建 or 增量更新)
# --force — 强制完整构建
# --restart — 仅重启容器(跳过构建)
# --init — 强制初次完整构建
# 不使用 set -e改用显式错误处理避免管道命令导致意外退出
# ============================================================
# 颜色和日志
# ============================================================
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; RED='\033[0;31m'; NC='\033[0m'
log() { printf "${GREEN}[✓]${NC} %s\n" "$1"; }
warn() { printf "${YELLOW}[!]${NC} %s\n" "$1"; }
info() { printf "${CYAN}[i]${NC} %s\n" "$1"; }
error() { printf "${RED}[✗]${NC} %s\n" "$1"; }
timing(){ printf " ${CYAN}${NC} %s\n" "$1"; }
# ============================================================
# 检测 macOS 和 Linux sed 差异
# ============================================================
is_mac() {
[[ "$(uname)" == "Darwin" ]]
}
# macOS 的 sed -i 需要空后缀 ''Linux 不需要
SED_I_BACKUP='-i'
if is_mac; then
SED_I_BACKUP='-i ""'
fi
# ============================================================
# 解析参数
# ============================================================
FORCE_BUILD=0; ONLY_RESTART=0; INIT_BUILD=0
while [[ $# -gt 0 ]]; do
case "$1" in
--force) FORCE_BUILD=1; info "模式: 强制完整构建"; shift ;;
--restart) ONLY_RESTART=1; info "模式: 仅重启容器"; shift ;;
--init) INIT_BUILD=1; info "模式: 初次完整构建"; shift ;;
*) warn "未知参数: $1"; shift ;;
esac
done
# ============================================================
# 选择站点
# ============================================================
echo ""
printf "${CYAN}=========================================${NC}\n"
printf "${CYAN} 选择要部署的站点 (tlyq.ai)${NC}\n"
printf "${CYAN}=========================================${NC}\n"
echo ""
echo " 1) www.tlyq.ai (图灵引擎官网)"
echo " 2) cloud.tlyq.ai (图灵智算系统云平台)"
echo " 3) token.tlyq.ai (Token工厂)"
echo " 4) issue.tlyq.ai (工单系统)"
echo " 5) assets.tlyq.ai (资产管理系统)"
echo " 6) oa.tlyq.ai (OA 统一门户 — 含 nginx 配置)"
echo " 7) ldap.tlyq.ai (LLDAP 用户目录服务)"
echo ""
printf "请输入编号 (1/2/3/4/5/6/7): "
read choice
SITE=""; LOCAL_DIR=""; REMOTE_DIR=""; CONTAINER=""
case "$choice" in
1) SITE="www"; LOCAL_DIR="/Users/niuniu/programs/docker/www-ai/src"; REMOTE_DIR="/tmp/project-extract-2/turing-engine" ;;
2) SITE="cloud"; LOCAL_DIR="/Users/niuniu/programs/docker/cloud-ai/html"; REMOTE_DIR="/root/docker/cloud-ai/html" ;;
3) SITE="token"; LOCAL_DIR="/Users/niuniu/programs/docker/token-ai/html"; REMOTE_DIR="/root/docker/token-ai/html" ;;
4) SITE="issue"; LOCAL_DIR="/Users/niuniu/programs/docker/issue-ai"; REMOTE_DIR="/root/docker/issue-ai"; CONTAINER="issue-ai" ;;
5) SITE="assets"; LOCAL_DIR="/Users/niuniu/programs/docker/assets-ai"; REMOTE_DIR="/root/docker/assets-ai"; CONTAINER="assets-ai" ;;
6) SITE="oa"; LOCAL_DIR="/Users/niuniu/programs/docker/oa-ai"; REMOTE_DIR="/root/docker/oa-ai"; CONTAINER="oa-ai" ;;
7) SITE="ldap"; LOCAL_DIR="/Users/niuniu/programs/docker/ldap-ai"; REMOTE_DIR="/root/docker/ldap-ai"; CONTAINER="lldap" ;;
*) echo "无效选择"; exit 1 ;;
esac
echo ""
log "部署目标: $LOCAL_DIR$REMOTE_DIR"
echo ""
# ============================================================
# 链接替换
# ============================================================
replace_url() {
local mode="$1"
local from to
if [[ "$mode" == "prod" ]]; then
from="localhost"; to="https://www.tlyq.ai"
else
from="https://www.tlyq.ai"; to="localhost"
fi
case "$SITE" in
www)
cd "$LOCAL_DIR"
find src -type f -name '*.tsx' -exec sed $SED_I_BACKUP \
-e "s|${from}:6174|https://cloud.tlyq.ai|g" \
-e "s|${from}:6175|https://token.tlyq.ai|g" \
-e "s|https://cloud.tlyq.ai|${from}:6174|g" \
-e "s|https://token.tlyq.ai|${from}:6175|g" \
{} + 2>/dev/null || true ;;
cloud)
sed $SED_I_BACKUP "s|${from}:6173|https://www.tlyq.ai|g" "$LOCAL_DIR/index.html" 2>/dev/null || true
sed $SED_I_BACKUP "s|https://www.tlyq.ai|${from}:6173|g" "$LOCAL_DIR/index.html" 2>/dev/null || true ;;
token)
sed $SED_I_BACKUP "s|${from}:6173|https://www.tlyq.ai|g" "$LOCAL_DIR/index.html" 2>/dev/null || true
sed $SED_I_BACKUP "s|https://www.tlyq.ai|${from}:6173|g" "$LOCAL_DIR/index.html" 2>/dev/null || true ;;
esac
}
# OA 专属 URL 替换(三类:前端导航 / 服务端内部 API / 退出登录)
replace_oa_urls() {
local mode="$1" # prod | local
cd "$LOCAL_DIR"
if [[ "$mode" == "prod" ]]; then
# A. 前端导航卡片 → 生产域名
sed $SED_I_BACKUP \
-e 's|http://localhost:6177|https://assets.tlyq.ai|g' \
-e 's|http://localhost:6176|https://issue.tlyq.ai|g' \
-e 's|http://localhost:6173|https://www.tlyq.ai|g' \
-e 's|http://localhost:6174|https://cloud.tlyq.ai|g' \
-e 's|http://localhost:6175|https://token.tlyq.ai|g' \
src/app/page.tsx
# B. 服务端内部 API 调用 → Docker 容器名assets-ai:3000 / issue-ai:3000
find src/app/api -name "*.ts" -exec sed $SED_I_BACKUP \
-e "s|'http://localhost:6177'|'http://assets-ai:3000'|g" \
-e "s|'http://localhost:6176'|'http://issue-ai:3000'|g" \
-e "s|\`http://localhost:\${site === 'assets' ? 6177 : 6176}\`|\`http://\${site}-ai:3000\`|g" \
{} +
# C. 退出登录重定向
sed $SED_I_BACKUP "s|'http://localhost:6179'|'https://oa.tlyq.ai'|g" src/app/api/auth/logout/route.ts
else
# 恢复本地 URL
sed $SED_I_BACKUP \
-e 's|https://assets.tlyq.ai|http://localhost:6177|g' \
-e 's|https://issue.tlyq.ai|http://localhost:6176|g' \
-e 's|https://www.tlyq.ai|http://localhost:6173|g' \
-e 's|https://cloud.tlyq.ai|http://localhost:6174|g' \
-e 's|https://token.tlyq.ai|http://localhost:6175|g' \
src/app/page.tsx
find src/app/api -name "*.ts" -exec sed $SED_I_BACKUP \
-e "s|'http://assets-ai:3000'|'http://localhost:6177'|g" \
-e "s|'http://issue-ai:3000'|'http://localhost:6176'|g" \
-e "s|\`http://\${site}-ai:3000\`|\`http://localhost:\${site === 'assets' ? 6177 : 6176}\`|g" \
{} +
sed $SED_I_BACKUP "s|'https://oa.tlyq.ai'|'http://localhost:6179'|g" src/app/api/auth/logout/route.ts
fi
}
to_prod() { [[ "$SITE" != "issue" && "$SITE" != "assets" && "$SITE" != "oa" ]] && replace_url prod; }
to_local() { [[ "$SITE" != "issue" && "$SITE" != "assets" && "$SITE" != "oa" ]] && replace_url local; }
trap 'to_local; [[ "$SITE" == "oa" ]] && replace_oa_urls local' EXIT
[[ "$SITE" == "oa" ]] && replace_oa_urls prod
[[ "$SITE" != "issue" && "$SITE" != "assets" && "$SITE" != "oa" ]] && to_prod
# ============================================================
# 核心构建函数(服务器上直接 npm run build
# ============================================================
SNAPSHOT_FILE="/tmp/.snapshot.${SITE}.md5"
BUILD_START=$(date +%s)
build_on_server() {
local extra_env="${1:-}"
# 1. 计算源码快照
log "检查源码是否有变化..."
local_md5=$(find "$LOCAL_DIR" \
-not -path '*/node_modules/*' \
-not -path '*/.next/*' \
-not -path '*/out/*' \
-not -path '*/data/*' \
-not -path '*/reports/*' \
-not -path '*/docs/*' \
-not -path '*/.env*' \
-not -path '*/.git/*' \
-not -name '._*' \
-type f \
-exec md5 -q {} \; \
| sort \
| md5 -q)
echo " 源码快照: ${local_md5}"
prev_md5=$(ssh txjp "cat ${SNAPSHOT_FILE} 2>/dev/null" 2>/dev/null || echo "")
if [[ "$FORCE_BUILD" == "1" || "$INIT_BUILD" == "1" ]]; then
info "强制构建模式"
MODE="force"
elif [[ -z "$prev_md5" ]]; then
info "首次部署,执行完整构建"
MODE="init"
elif [[ "$prev_md5" == "$local_md5" ]]; then
info "源码无变化,跳过构建,仅重启容器"
MODE="skip"
else
info "检测到源码变化,执行增量构建"
MODE="update"
fi
if [[ "$ONLY_RESTART" == "1" ]]; then
MODE="restart"
info "强制重建容器,跳过构建"
ssh txjp "cd $REMOTE_DIR && docker compose down && docker compose up -d"
return 0
fi
if [[ "$MODE" == "skip" || "$MODE" == "restart" ]]; then
ssh txjp "cd $REMOTE_DIR && docker compose down && docker compose up -d"
log "容器已重建"
return 0
fi
# 2. 打包源码(不含 node_modules
log "打包源码..."
pack_start=$(date +%s)
COPYFILE_DISABLE=1 tar czf /tmp/${SITE}.tar.gz -C "$LOCAL_DIR" \
--exclude='node_modules' --exclude='.next' --exclude='out' \
--exclude='data' --exclude='./reports/*.docx' --exclude='docs' \
--exclude='.env' --exclude='.env.local' --exclude='.env.development' --exclude='.env.production' \
--exclude='.git' --exclude='._*' . || {
error "打包失败"
return 1
}
pack_end=$(date +%s)
pack_dur=$((pack_end - pack_start))
local pkg_size
pkg_size=$(du -h /tmp/${SITE}.tar.gz 2>/dev/null | cut -f1 || echo "未知")
timing "打包耗时: ${pack_dur}s (${pkg_size})"
# 3. 上传
log "上传源码包..."
scp /tmp/${SITE}.tar.gz txjp:/tmp/${SITE}.tar.gz || {
error "上传失败"
return 1
}
# 4. 解压源码到 /tmp独立于目标目录避免 rsync --delete 误删源文件)
log "解压源码并准备依赖..."
ssh txjp "\
rm -rf /tmp/deploy_${SITE} && mkdir -p /tmp/deploy_${SITE} && \
tar xzf /tmp/${SITE}.tar.gz -C /tmp/deploy_${SITE} 2>/dev/null || true && \
rsync -a --delete \
--exclude='node_modules' --exclude='.next' --exclude='data' --exclude='/reports/*.docx' --exclude='docs' \
--exclude='.env' --exclude='.env.local' --exclude='.env.development' --exclude='.env.production' \
--exclude='._*' \
/tmp/deploy_${SITE}/ ${REMOTE_DIR}/ && \
rm -rf /tmp/deploy_${SITE}"
# 首次:上传 docker-compose.yml新增 .next 卷挂载),重启容器应用
ssh txjp "cd $REMOTE_DIR && docker compose up -d"
# 如果 host 上没有 node_modules从容器内复制一份
ssh txjp "if [ ! -d '${REMOTE_DIR}/node_modules' ]; then
echo ' 首次:复制容器内 node_modules 到主机...'
docker cp ${CONTAINER}:/app/node_modules ${REMOTE_DIR}/node_modules
echo ' 完成'
fi"
# 安装可能新增的依赖(已有依赖走缓存,很快)
log "安装新增依赖..."
ssh txjp "cd ${REMOTE_DIR} && npm install --prefer-offline 2>&1 | tail -5 || true"
if [[ -n "$extra_env" ]]; then
ssh txjp "sed -i 's|ASSETS_API_URL=.*|ASSETS_API_URL=${extra_env}|' $REMOTE_DIR/.env 2>/dev/null \
|| echo 'ASSETS_API_URL=${extra_env}' >> $REMOTE_DIR/.env"
fi
# assets 站点需要 NEXT_PUBLIC_ISSUE_URL构建时内嵌到 JS bundle和 ISSUE_API_URL运行时容器内网地址
if [[ "$SITE" == "assets" ]]; then
ssh txjp "grep -q 'NEXT_PUBLIC_ISSUE_URL' $REMOTE_DIR/.env 2>/dev/null \
&& sed -i 's|NEXT_PUBLIC_ISSUE_URL=.*|NEXT_PUBLIC_ISSUE_URL=https://issue.tlyq.ai/tickets|' $REMOTE_DIR/.env 2>/dev/null \
|| echo 'NEXT_PUBLIC_ISSUE_URL=https://issue.tlyq.ai/tickets' >> $REMOTE_DIR/.env"
ssh txjp "grep -q 'ISSUE_API_URL' $REMOTE_DIR/.env 2>/dev/null \
&& sed -i 's|ISSUE_API_URL=.*|ISSUE_API_URL=http://issue-ai:3000/api|' $REMOTE_DIR/.env 2>/dev/null \
|| echo 'ISSUE_API_URL=http://issue-ai:3000/api' >> $REMOTE_DIR/.env"
fi
# issue 站点需要 NEXT_PUBLIC_ASSETS_URL构建时内嵌到 JS bundle
if [[ "$SITE" == "issue" ]]; then
ssh txjp "grep -q 'NEXT_PUBLIC_ASSETS_URL' $REMOTE_DIR/.env 2>/dev/null \
&& sed -i 's|NEXT_PUBLIC_ASSETS_URL=.*|NEXT_PUBLIC_ASSETS_URL=https://assets.tlyq.ai|' $REMOTE_DIR/.env 2>/dev/null \
|| echo 'NEXT_PUBLIC_ASSETS_URL=https://assets.tlyq.ai' >> $REMOTE_DIR/.env"
fi
# 确保 AUTHELIA_URL 已设置(退出登录 cookie domain 依赖此变量)
ssh txjp "grep -q 'AUTHELIA_URL' $REMOTE_DIR/.env 2>/dev/null \
&& sed -i 's|AUTHELIA_URL=.*|AUTHELIA_URL=https://sso.tlyq.ai|' $REMOTE_DIR/.env 2>/dev/null \
|| echo 'AUTHELIA_URL=https://sso.tlyq.ai' >> $REMOTE_DIR/.env"
# 清理可能被上传的本地环境变量文件(.env.local 等会覆盖 .env 中的生产配置)
ssh txjp "rm -f $REMOTE_DIR/.env.local $REMOTE_DIR/.env.development $REMOTE_DIR/.env.production 2>/dev/null; echo '已清理本地环境文件'"
# 5. 服务器上直接 npm run build不重建 Docker
log "服务器上执行 npm run build..."
build_start=$(date +%s)
# 使用 tee 避免管道导致退出码丢失,并过滤无用输出
ssh txjp "cd ${REMOTE_DIR} && npm run build 2>&1" | \
grep -vE "^(info|warn|npm warn|audited|packages|funding|vulnerability|npm notice|New major)" | \
tee /tmp/build_output.txt | \
tail -15
build_exit_code=${PIPESTATUS[0]}
build_end=$(date +%s)
build_dur=$((build_end - build_start))
timing "构建耗时: ${build_dur}s"
if [[ "$build_exit_code" -ne 0 ]]; then
warn "构建可能有警告,请检查上面的输出"
fi
# 清理 standalone 中的 musl 原生模块Alpine builder 残留Debian runner 不兼容)
if [[ "$SITE" == "issue" || "$SITE" == "assets" || "$SITE" == "oa" ]]; then
ssh txjp "rm -rf $REMOTE_DIR/.next/standalone/node_modules/@img/sharp-linuxmusl-x64 \
$REMOTE_DIR/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64 \
$REMOTE_DIR/.next/standalone/node_modules/@next/swc-linux-x64-musl 2>/dev/null; echo '已清理 musl 残留'"
fi
# 6. 重建容器(应用 .next 卷挂载;若存在 Dockerfile 则 --build 确保原生模块兼容;
# 无论有无 Dockerfilerestart 确保 Next.js 进程重新加载 .next
log "重建容器..."
local build_flag=""
ssh txjp "test -f $REMOTE_DIR/Dockerfile" 2>/dev/null && build_flag="--build"
ssh txjp "cd $REMOTE_DIR && docker compose up -d $build_flag && docker compose restart"
# 7. 保存快照
ssh txjp "echo '$local_md5' > ${SNAPSHOT_FILE}"
}
# ============================================================
# 执行部署
# ============================================================
echo ""
case "$SITE" in
www)
build_on_server ;;
cloud)
info "上传静态文件..."
scp "$LOCAL_DIR"/index.html txjp:$REMOTE_DIR/index.html 2>/dev/null || true
ssh txjp "docker restart ${SITE}-ai" ;;
token)
info "上传静态文件..."
scp "$LOCAL_DIR"/index.html txjp:$REMOTE_DIR/index.html 2>/dev/null || true
ssh txjp "docker restart ${SITE}-ai" ;;
issue)
build_on_server "http://assets-ai:3000/api" || exit 1
info "部署 nginx 路由..."
ssh txjp "cp $REMOTE_DIR/../nginx-proxy-ai/conf.d/issue-ai.conf /root/docker/nginx-proxy-ai/conf.d/ 2>/dev/null || true
docker exec nginx-ai nginx -s reload 2>/dev/null || true"
# 验证 issue→assets API 连通性(带重试,应对同时部署时目标容器暂不可达)
info "检查 issue→assets API 连通性..."
ssh txjp 'cat > /tmp/check-conn.js' << 'CHECKEOF'
const http = require("http");
const url = process.env.ASSETS_API_URL + "/assets?pageSize=1";
const key = process.env.ASSETS_API_KEY || "";
let attempt = 0;
function tryConnect() {
attempt++;
http.get(url, {headers:{"Authorization":"Bearer "+key}}, (res) => {
if (res.statusCode === 200) { console.log("连通正常: HTTP 200 (第"+attempt+"次)"); process.exit(0); }
console.error("连通失败: HTTP", res.statusCode, "(第"+attempt+"次)");
if (attempt < 3) { console.error("10s 后重试..."); setTimeout(tryConnect, 10000); }
else { console.error("已达最大重试次数,请检查 ASSETS_API_KEY 是否在 assets-ai 中注册"); process.exit(1); }
}).on("error", (e) => {
console.error("连接失败:", e.message, "(第"+attempt+"次)");
if (attempt < 3) { console.error("10s 后重试..."); setTimeout(tryConnect, 10000); }
else { console.error("已达最大重试次数"); process.exit(1); }
});
}
tryConnect();
CHECKEOF
CONN_CHECK=$(ssh txjp "docker cp /tmp/check-conn.js issue-ai:/tmp/check-conn.js && docker exec issue-ai sh -c 'cd /app && NODE_PATH=/app/node_modules node /tmp/check-conn.js'" 2>&1)
if [ $? -ne 0 ]; then
error "issue→assets API 连通性检查失败!请检查 ASSETS_API_KEY 是否在 assets-ai 中注册"
echo " $CONN_CHECK"
exit 1
fi
log "issue→assets API 连通性正常" ;;
assets)
build_on_server || exit 1
info "部署 nginx 路由..."
ssh txjp "cp $REMOTE_DIR/../nginx-proxy-ai/conf.d/assets-ai.conf /root/docker/nginx-proxy-ai/conf.d/ 2>/dev/null || true
docker exec nginx-ai nginx -s reload 2>/dev/null || true"
# 验证 assets→issue API 连通性(带重试,应对同时部署时目标容器暂不可达)
info "检查 assets→issue API 连通性..."
ssh txjp 'cat > /tmp/check-conn.js' << 'CHECKEOF'
const http = require("http");
const url = process.env.ISSUE_API_URL + "/tickets?pageSize=1";
const key = process.env.ISSUE_API_KEY || "";
let attempt = 0;
function tryConnect() {
attempt++;
http.get(url, {headers:{"Authorization":"Bearer "+key}}, (res) => {
if (res.statusCode === 200) { console.log("连通正常: HTTP 200 (第"+attempt+"次)"); process.exit(0); }
console.error("连通失败: HTTP", res.statusCode, "(第"+attempt+"次)");
if (attempt < 3) { console.error("10s 后重试..."); setTimeout(tryConnect, 10000); }
else { console.error("已达最大重试次数,请检查 ISSUE_API_KEY 是否在 issue-ai 中注册"); process.exit(1); }
}).on("error", (e) => {
console.error("连接失败:", e.message, "(第"+attempt+"次)");
if (attempt < 3) { console.error("10s 后重试..."); setTimeout(tryConnect, 10000); }
else { console.error("已达最大重试次数"); process.exit(1); }
});
}
tryConnect();
CHECKEOF
CONN_CHECK=$(ssh txjp "docker cp /tmp/check-conn.js assets-ai:/tmp/check-conn.js && docker exec assets-ai sh -c 'cd /app && NODE_PATH=/app/node_modules node /tmp/check-conn.js'" 2>&1)
if [ $? -ne 0 ]; then
error "assets→issue API 连通性检查失败!请检查 ISSUE_API_KEY 是否在 issue-ai 中注册"
echo " $CONN_CHECK"
exit 1
fi
log "assets→issue API 连通性正常" ;;
oa)
build_on_server || exit 1
info "部署 nginx 配置..."
ssh txjp "cp $REMOTE_DIR/../nginx-proxy-ai/conf.d/oa-ai.conf /root/docker/nginx-proxy-ai/conf.d/ 2>/dev/null || true
docker exec nginx-ai nginx -t && docker exec nginx-ai nginx -s reload" ;;
ldap)
info "部署 LLDAP..."
scp "$LOCAL_DIR/docker-compose.yml" txjp:$REMOTE_DIR/docker-compose.yml
scp "$LOCAL_DIR/Dockerfile" txjp:$REMOTE_DIR/Dockerfile
ssh txjp "cd $REMOTE_DIR && \
if [ ! -f .env ]; then \
echo 'LLDAP_JWT_SECRET='\$(docker exec lldap printenv LLDAP_JWT_SECRET 2>/dev/null) > .env && \
echo 'LLDAP_LDAP_USER_PASS='\$(docker exec lldap printenv LLDAP_LDAP_USER_PASS 2>/dev/null) >> .env && \
echo 'LLDAP_LDAP_BASE_DN=dc=tlyq,dc=ai' >> .env && \
echo 'LLDAP_HTTP_PORT=17170' >> .env && \
echo 'LLDAP_LDAP_PORT=3890' >> .env && \
echo 'LLDAP_DATABASE_PATH=/data/users.db' >> .env && \
echo 'LLDAP_ADMIN_PASSWORD='\$(docker exec lldap printenv LLDAP_ADMIN_PASSWORD 2>/dev/null) >> .env && \
fi && \
sed -i '/3890:3890/d' docker-compose.yml 2>/dev/null || true && \
docker compose up -d --build && docker compose restart"
log "LLDAP 已部署" ;;
esac
# ============================================================
# 验证
# ============================================================
echo ""
log "验证部署..."
BUILD_END=$(date +%s)
TOTAL_DUR=$((BUILD_END - BUILD_START))
URL=""
case "$SITE" in
www) URL="https://www.tlyq.ai" ;;
cloud) URL="https://cloud.tlyq.ai" ;;
token) URL="https://token.tlyq.ai" ;;
issue) URL="https://issue.tlyq.ai" ;;
assets) URL="https://assets.tlyq.ai" ;;
oa) URL="https://oa.tlyq.ai" ;;
ldap) URL="http://localhost:6178" ;;
esac
if [[ "$SITE" == "ldap" ]]; then
STATUS=$(ssh txjp "docker exec lldap wget -q -O - http://127.0.0.1:17170 > /dev/null 2>&1 && echo 200 || echo 000" 2>/dev/null || echo "???")
else
STATUS=$(ssh txjp "curl -s -o /dev/null -w '%{http_code}' -k '$URL' 2>/dev/null" 2>/dev/null || echo "???")
fi
if [[ "$STATUS" == "200" || "$STATUS" == "307" || "$STATUS" == "301" ]]; then
log "部署成功!总耗时: ${TOTAL_DUR}s | 访问 $URL"
exit 0
else
warn "返回状态码: $STATUS,请检查"
exit 1
fi