#!/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 "$@"