|
|
#!/usr/bin/env bash
|
|
|
# 查价中台 — 生产环境一键部署(Linux)
|
|
|
# 用法:
|
|
|
# cp deploy/.env.production.example .env # 编辑必填项
|
|
|
# bash deploy/install.sh
|
|
|
# bash deploy/install.sh --nginx-http # 额外安装 Nginx HTTP 反代
|
|
|
# bash deploy/install.sh --nginx-ssl # 额外安装 Nginx HTTPS 反代(需已有证书)
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
|
COMPOSE_FILE="${ROOT_DIR}/deploy/docker-compose.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]${NC} $*"; }
|
|
|
warn() { echo -e "${YELLOW}[deploy]${NC} $*"; }
|
|
|
err() { echo -e "${RED}[deploy]${NC} $*" >&2; }
|
|
|
|
|
|
require_cmd() {
|
|
|
if ! command -v "$1" >/dev/null 2>&1; then
|
|
|
err "缺少命令: $1"
|
|
|
exit 1
|
|
|
fi
|
|
|
}
|
|
|
|
|
|
validate_env() {
|
|
|
if [[ ! -f "$ENV_FILE" ]]; then
|
|
|
err "未找到 .env,请先执行:cp deploy/.env.production.example .env"
|
|
|
exit 1
|
|
|
fi
|
|
|
|
|
|
# shellcheck disable=SC1090
|
|
|
set -a
|
|
|
source "$ENV_FILE"
|
|
|
set +a
|
|
|
|
|
|
local missing=0
|
|
|
for key in MYSQL_ROOT_PASSWORD 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
|
|
|
|
|
|
if [[ -z "${MOTHERSHIP_QUOTE_URLS:-}" ]]; then
|
|
|
err "请在 .env 中填写 MOTHERSHIP_QUOTE_URLS"
|
|
|
missing=1
|
|
|
fi
|
|
|
|
|
|
if [[ $missing -ne 0 ]]; then
|
|
|
exit 1
|
|
|
fi
|
|
|
}
|
|
|
|
|
|
install_nginx() {
|
|
|
local mode="$1"
|
|
|
local template=""
|
|
|
local domain="${PUBLIC_DOMAIN:-quote.example.com}"
|
|
|
|
|
|
if [[ "$mode" == "http" ]]; then
|
|
|
template="${ROOT_DIR}/deploy/nginx/chajia.http.conf"
|
|
|
elif [[ "$mode" == "ssl" ]]; then
|
|
|
template="${ROOT_DIR}/deploy/nginx/chajia.ssl.conf"
|
|
|
else
|
|
|
err "未知 nginx 模式: $mode"
|
|
|
exit 1
|
|
|
fi
|
|
|
|
|
|
require_cmd nginx
|
|
|
local tmp="/tmp/chajia-nginx-$$.conf"
|
|
|
sed "s/quote\.example\.com/${domain}/g" "$template" > "$tmp"
|
|
|
|
|
|
if [[ -d /etc/nginx/sites-available ]]; then
|
|
|
sudo cp "$tmp" /etc/nginx/sites-available/chajia
|
|
|
sudo ln -sf /etc/nginx/sites-available/chajia /etc/nginx/sites-enabled/chajia
|
|
|
sudo rm -f /etc/nginx/sites-enabled/default 2>/dev/null || true
|
|
|
elif [[ -d /etc/nginx/conf.d ]]; then
|
|
|
sudo cp "$tmp" /etc/nginx/conf.d/chajia.conf
|
|
|
else
|
|
|
err "未找到 /etc/nginx/sites-available 或 /etc/nginx/conf.d"
|
|
|
rm -f "$tmp"
|
|
|
exit 1
|
|
|
fi
|
|
|
rm -f "$tmp"
|
|
|
|
|
|
sudo nginx -t
|
|
|
sudo systemctl reload nginx
|
|
|
log "Nginx 已配置(${mode}),域名: ${domain}"
|
|
|
}
|
|
|
|
|
|
deploy_docker() {
|
|
|
require_cmd docker
|
|
|
if ! docker compose version >/dev/null 2>&1; then
|
|
|
err "需要 Docker Compose V2(docker compose)"
|
|
|
exit 1
|
|
|
fi
|
|
|
|
|
|
cd "$ROOT_DIR"
|
|
|
log "构建并启动容器..."
|
|
|
docker compose -f "$COMPOSE_FILE" up -d --build
|
|
|
|
|
|
log "等待 MySQL 就绪..."
|
|
|
sleep 5
|
|
|
|
|
|
log "执行数据库迁移..."
|
|
|
docker compose -f "$COMPOSE_FILE" run --rm --no-deps rpa-worker npx prisma migrate deploy
|
|
|
|
|
|
log "初始化种子数据(管理员账号)..."
|
|
|
docker compose -f "$COMPOSE_FILE" run --rm --no-deps rpa-worker npx tsx prisma/seed.ts 2>/dev/null \
|
|
|
|| warn "seed 已存在或跳过(默认账号 admin_demo / Demo@123)"
|
|
|
|
|
|
log "容器状态:"
|
|
|
docker compose -f "$COMPOSE_FILE" ps
|
|
|
}
|
|
|
|
|
|
smoke_check() {
|
|
|
local port="${APP_PORT:-3000}"
|
|
|
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.yml logs -f next-app"
|
|
|
fi
|
|
|
}
|
|
|
|
|
|
print_summary() {
|
|
|
local port="${APP_PORT:-3000}"
|
|
|
local domain="${PUBLIC_DOMAIN:-quote.example.com}"
|
|
|
echo ""
|
|
|
log "========== 部署完成 =========="
|
|
|
echo " 本地访问: http://127.0.0.1:${port}"
|
|
|
echo " 对外域名: https://${domain} (需配置 Nginx + 证书)"
|
|
|
echo " 管理端: /admin/alerts (账号见 prisma seed: admin_demo / Demo@123,请尽快改密)"
|
|
|
echo ""
|
|
|
echo " 外部系统 API:"
|
|
|
echo " POST /api/addresses/mothership-candidates"
|
|
|
echo " POST /api/quotes"
|
|
|
echo " GET /api/quotes/{quote_id}"
|
|
|
echo ""
|
|
|
echo " 常用命令:"
|
|
|
echo " docker compose -f deploy/docker-compose.yml logs -f rpa-worker"
|
|
|
echo " docker compose -f deploy/docker-compose.yml restart"
|
|
|
echo " docker compose -f deploy/docker-compose.yml down"
|
|
|
echo ""
|
|
|
warn "【人工必做】见 deploy/README.md「部署后检查清单」"
|
|
|
}
|
|
|
|
|
|
main() {
|
|
|
local nginx_mode=""
|
|
|
for arg in "$@"; do
|
|
|
case "$arg" in
|
|
|
--nginx-http) nginx_mode="http" ;;
|
|
|
--nginx-ssl) nginx_mode="ssl" ;;
|
|
|
--help|-h)
|
|
|
echo "用法: bash deploy/install.sh [--nginx-http|--nginx-ssl]"
|
|
|
exit 0
|
|
|
;;
|
|
|
esac
|
|
|
done
|
|
|
|
|
|
validate_env
|
|
|
deploy_docker
|
|
|
smoke_check
|
|
|
|
|
|
if [[ -n "$nginx_mode" ]]; then
|
|
|
install_nginx "$nginx_mode"
|
|
|
else
|
|
|
warn "未安装 Nginx。公网访问请执行: bash deploy/install.sh --nginx-http"
|
|
|
fi
|
|
|
|
|
|
print_summary
|
|
|
}
|
|
|
|
|
|
main "$@"
|