#!/bin/bash #=============================================================================== # iConsulting 部署管理脚本 # # 用法: ./deploy.sh [service] [options] # # 命令: # build - 编译构建 # start - 启动服务 # stop - 停止服务 # restart - 重启服务 # status - 查看状态 # logs - 查看日志 # clean - 清理构建产物 # deploy - 完整部署(构建+启动) # db - 数据库操作 # help - 显示帮助 # # 服务: # all - 所有服务 # web-client - 用户前端 # admin-client - 管理后台前端 # conversation - 对话服务 # user - 用户服务 # payment - 支付服务 # knowledge - 知识库服务 # evolution - 进化服务 # kong - API网关 # postgres - PostgreSQL数据库 # redis - Redis缓存 # neo4j - Neo4j图数据库 # nginx - Nginx静态服务 # #=============================================================================== set -e # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' NC='\033[0m' # No Color # 项目根目录 PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$PROJECT_ROOT" # 配置 COMPOSE_FILE="docker-compose.yml" ENV_FILE=".env" # 域名配置 DOMAIN="${DOMAIN:-iconsulting.szaiai.com}" ADMIN_EMAIL="${ADMIN_EMAIL:-admin@szaiai.com}" # 服务端口配置 declare -A SERVICE_PORTS=( ["conversation"]=3004 ["user"]=3001 ["payment"]=3002 ["knowledge"]=3003 ["evolution"]=3005 ["kong"]=8000 ["postgres"]=5432 ["redis"]=6379 ["neo4j"]=7474 ["nginx"]=80 ) # 服务目录映射 declare -A SERVICE_DIRS=( ["conversation"]="packages/services/conversation-service" ["user"]="packages/services/user-service" ["payment"]="packages/services/payment-service" ["knowledge"]="packages/services/knowledge-service" ["evolution"]="packages/services/evolution-service" ["web-client"]="packages/web-client" ["admin-client"]="packages/admin-client" ["shared"]="packages/shared" ) # Docker服务名映射 declare -A DOCKER_SERVICES=( ["conversation"]="conversation-service" ["user"]="user-service" ["payment"]="payment-service" ["knowledge"]="knowledge-service" ["evolution"]="evolution-service" ["web-client"]="web-client" ["admin-client"]="admin-client" ["kong"]="kong" ["postgres"]="postgres" ["redis"]="redis" ["neo4j"]="neo4j" ["nginx"]="nginx" ) #=============================================================================== # 工具函数 #=============================================================================== log_info() { echo -e "${BLUE}[INFO]${NC} $1" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } log_step() { echo -e "${PURPLE}[STEP]${NC} $1" } # 检查命令是否存在 check_command() { if ! command -v "$1" &> /dev/null; then log_error "$1 未安装,请先安装" exit 1 fi } # 检查环境 check_environment() { log_step "检查运行环境..." check_command "node" check_command "pnpm" check_command "docker" check_command "docker-compose" # 检查 Node 版本 NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1) if [ "$NODE_VERSION" -lt 18 ]; then log_error "Node.js 版本需要 >= 18,当前版本: $(node -v)" exit 1 fi log_success "环境检查通过" } # 加载环境变量 load_env() { if [ -f "$ENV_FILE" ]; then export $(grep -v '^#' "$ENV_FILE" | xargs) fi } # 等待服务就绪 wait_for_service() { local host=$1 local port=$2 local service=$3 local max_attempts=${4:-30} local attempt=1 log_info "等待 $service ($host:$port) 就绪..." while [ $attempt -le $max_attempts ]; do if nc -z "$host" "$port" 2>/dev/null; then log_success "$service 已就绪" return 0 fi echo -n "." sleep 2 attempt=$((attempt + 1)) done echo "" log_error "$service 启动超时" return 1 } #=============================================================================== # 构建函数 #=============================================================================== # 安装依赖 install_deps() { log_step "安装项目依赖..." pnpm install log_success "依赖安装完成" } # 构建共享包 build_shared() { log_step "构建 shared 包..." cd "$PROJECT_ROOT/${SERVICE_DIRS[shared]}" pnpm run build cd "$PROJECT_ROOT" log_success "shared 构建完成" } # 构建单个后端服务 build_backend_service() { local service=$1 local dir="${SERVICE_DIRS[$service]}" if [ -z "$dir" ]; then log_error "未知服务: $service" return 1 fi log_step "构建 $service..." cd "$PROJECT_ROOT/$dir" # 清理旧构建 rm -rf dist # TypeScript 编译 pnpm run build cd "$PROJECT_ROOT" log_success "$service 构建完成" } # 构建单个前端 build_frontend() { local service=$1 local dir="${SERVICE_DIRS[$service]}" if [ -z "$dir" ]; then log_error "未知服务: $service" return 1 fi log_step "构建 $service..." cd "$PROJECT_ROOT/$dir" # 清理旧构建 rm -rf dist # Vite 构建 pnpm run build cd "$PROJECT_ROOT" log_success "$service 构建完成" } # 构建所有后端服务 build_all_backend() { build_shared for service in conversation user payment knowledge evolution; do build_backend_service "$service" done } # 构建所有前端 build_all_frontend() { for service in web-client admin-client; do build_frontend "$service" done } # 构建所有 build_all() { log_info "开始构建所有服务..." install_deps build_all_backend build_all_frontend log_success "所有服务构建完成" } # 构建入口 do_build() { local target=${1:-all} case $target in all) build_all ;; shared) build_shared ;; backend) build_all_backend ;; frontend) build_all_frontend ;; web-client|admin-client) build_frontend "$target" ;; conversation|user|payment|knowledge|evolution) build_backend_service "$target" ;; *) log_error "未知构建目标: $target" exit 1 ;; esac } #=============================================================================== # Docker 操作函数 #=============================================================================== # 构建 Docker 镜像 build_docker_images() { local service=${1:-} log_step "构建 Docker 镜像..." if [ -n "$service" ] && [ "$service" != "all" ]; then local docker_service="${DOCKER_SERVICES[$service]}" if [ -n "$docker_service" ]; then docker-compose build "$docker_service" else log_error "未知服务: $service" return 1 fi else docker-compose build fi log_success "Docker 镜像构建完成" } # 启动基础设施 start_infrastructure() { log_step "启动基础设施服务..." docker-compose up -d postgres redis neo4j # 等待数据库就绪 wait_for_service localhost 5432 "PostgreSQL" wait_for_service localhost 6379 "Redis" wait_for_service localhost 7474 "Neo4j" log_success "基础设施启动完成" } # 启动 Kong 网关 start_kong() { log_step "启动 Kong API 网关..." docker-compose up -d kong-database sleep 5 # Kong 数据库迁移 docker-compose run --rm kong kong migrations bootstrap || true docker-compose up -d kong wait_for_service localhost 8000 "Kong" log_success "Kong 启动完成" } # 启动后端服务 (非 Docker 模式) start_backend_service_local() { local service=$1 local dir="${SERVICE_DIRS[$service]}" local port="${SERVICE_PORTS[$service]}" if [ -z "$dir" ]; then log_error "未知服务: $service" return 1 fi log_step "启动 $service (端口: $port)..." cd "$PROJECT_ROOT/$dir" # 检查是否已构建 if [ ! -d "dist" ]; then log_warning "$service 未构建,先进行构建..." pnpm run build fi # 使用 PM2 或直接启动 if command -v pm2 &> /dev/null; then pm2 start dist/main.js --name "iconsulting-$service" --cwd "$PROJECT_ROOT/$dir" else # 后台启动 nohup node dist/main.js > "$PROJECT_ROOT/logs/$service.log" 2>&1 & echo $! > "$PROJECT_ROOT/pids/$service.pid" fi cd "$PROJECT_ROOT" sleep 2 wait_for_service localhost "$port" "$service" 15 } # 启动后端服务 (Docker 模式) start_backend_service_docker() { local service=$1 local docker_service="${DOCKER_SERVICES[$service]}" log_step "启动 $service (Docker)..." docker-compose up -d "$docker_service" local port="${SERVICE_PORTS[$service]}" wait_for_service localhost "$port" "$service" } # 启动所有后端服务 start_all_backend() { local mode=${1:-docker} for service in user payment knowledge conversation evolution; do if [ "$mode" = "docker" ]; then start_backend_service_docker "$service" else start_backend_service_local "$service" fi done } # 启动 Nginx (静态文件服务) start_nginx() { log_step "启动 Nginx..." docker-compose up -d nginx wait_for_service localhost 80 "Nginx" log_success "Nginx 启动完成" } # 启动所有服务 start_all() { local mode=${1:-docker} log_info "开始启动所有服务 (模式: $mode)..." # 创建必要目录 mkdir -p "$PROJECT_ROOT/logs" mkdir -p "$PROJECT_ROOT/pids" start_infrastructure start_kong start_all_backend "$mode" start_nginx log_success "所有服务启动完成" do_status } # 启动入口 do_start() { local target=${1:-all} local mode=${2:-docker} load_env case $target in all) start_all "$mode" ;; infra|infrastructure) start_infrastructure ;; kong) start_kong ;; nginx) start_nginx ;; postgres|redis|neo4j) docker-compose up -d "$target" ;; conversation|user|payment|knowledge|evolution) if [ "$mode" = "docker" ]; then start_backend_service_docker "$target" else start_backend_service_local "$target" fi ;; backend) start_all_backend "$mode" ;; *) log_error "未知启动目标: $target" exit 1 ;; esac } #=============================================================================== # 停止函数 #=============================================================================== # 停止单个服务 (本地模式) stop_service_local() { local service=$1 log_step "停止 $service..." if command -v pm2 &> /dev/null; then pm2 stop "iconsulting-$service" 2>/dev/null || true pm2 delete "iconsulting-$service" 2>/dev/null || true else local pid_file="$PROJECT_ROOT/pids/$service.pid" if [ -f "$pid_file" ]; then kill $(cat "$pid_file") 2>/dev/null || true rm -f "$pid_file" fi fi log_success "$service 已停止" } # 停止单个服务 (Docker 模式) stop_service_docker() { local service=$1 local docker_service="${DOCKER_SERVICES[$service]}" if [ -n "$docker_service" ]; then log_step "停止 $service..." docker-compose stop "$docker_service" log_success "$service 已停止" fi } # 停止所有服务 stop_all() { local mode=${1:-docker} log_info "停止所有服务..." if [ "$mode" = "docker" ]; then docker-compose down else for service in conversation user payment knowledge evolution; do stop_service_local "$service" done docker-compose down fi log_success "所有服务已停止" } # 停止入口 do_stop() { local target=${1:-all} local mode=${2:-docker} case $target in all) stop_all "$mode" ;; infra|infrastructure) docker-compose stop postgres redis neo4j ;; conversation|user|payment|knowledge|evolution) if [ "$mode" = "docker" ]; then stop_service_docker "$target" else stop_service_local "$target" fi ;; kong|postgres|redis|neo4j|nginx) docker-compose stop "$target" ;; *) log_error "未知停止目标: $target" exit 1 ;; esac } #=============================================================================== # 重启函数 #=============================================================================== do_restart() { local target=${1:-all} local mode=${2:-docker} log_info "重启 $target..." do_stop "$target" "$mode" sleep 2 do_start "$target" "$mode" } #=============================================================================== # 状态查看 #=============================================================================== do_status() { echo "" echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}" echo -e "${CYAN} iConsulting 服务状态 ${NC}" echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}" echo "" # Docker 服务状态 echo -e "${PURPLE}Docker 容器状态:${NC}" docker-compose ps echo "" # 端口检查 echo -e "${PURPLE}服务端口检查:${NC}" printf "%-20s %-10s %-10s\n" "服务" "端口" "状态" echo "----------------------------------------" for service in "${!SERVICE_PORTS[@]}"; do local port="${SERVICE_PORTS[$service]}" if nc -z localhost "$port" 2>/dev/null; then printf "%-20s %-10s ${GREEN}%-10s${NC}\n" "$service" "$port" "运行中" else printf "%-20s %-10s ${RED}%-10s${NC}\n" "$service" "$port" "未运行" fi done echo "" # PM2 状态 (如果使用) if command -v pm2 &> /dev/null; then echo -e "${PURPLE}PM2 进程状态:${NC}" pm2 list 2>/dev/null | grep iconsulting || echo "无 PM2 管理的服务" echo "" fi } #=============================================================================== # 日志查看 #=============================================================================== do_logs() { local service=${1:-all} local lines=${2:-100} if [ "$service" = "all" ]; then docker-compose logs -f --tail="$lines" else local docker_service="${DOCKER_SERVICES[$service]}" if [ -n "$docker_service" ]; then docker-compose logs -f --tail="$lines" "$docker_service" else # 本地日志 local log_file="$PROJECT_ROOT/logs/$service.log" if [ -f "$log_file" ]; then tail -f -n "$lines" "$log_file" else log_error "日志文件不存在: $log_file" fi fi fi } #=============================================================================== # 清理函数 #=============================================================================== do_clean() { local target=${1:-build} case $target in build) log_step "清理构建产物..." for dir in "${SERVICE_DIRS[@]}"; do rm -rf "$PROJECT_ROOT/$dir/dist" done log_success "构建产物已清理" ;; deps) log_step "清理依赖..." rm -rf node_modules for dir in "${SERVICE_DIRS[@]}"; do rm -rf "$PROJECT_ROOT/$dir/node_modules" done log_success "依赖已清理" ;; docker) log_step "清理 Docker 资源..." docker-compose down -v --rmi local docker system prune -f log_success "Docker 资源已清理" ;; logs) log_step "清理日志..." rm -rf "$PROJECT_ROOT/logs/*" log_success "日志已清理" ;; all) do_clean build do_clean deps do_clean docker do_clean logs ;; *) log_error "未知清理目标: $target (可选: build, deps, docker, logs, all)" exit 1 ;; esac } #=============================================================================== # 完整部署 #=============================================================================== do_deploy() { local mode=${1:-docker} log_info "开始完整部署 (模式: $mode)..." check_environment # 构建 do_build all # 如果是 Docker 模式,构建镜像 if [ "$mode" = "docker" ]; then build_docker_images fi # 启动 do_start all "$mode" # 配置 Kong 路由 setup_kong_routes log_success "部署完成!" echo "" echo -e "${CYAN}访问地址:${NC}" echo " 用户前端: https://$DOMAIN" echo " 管理后台: https://$DOMAIN/admin" echo " API 网关: https://$DOMAIN/api" echo " Kong 管理: http://localhost:8001 (仅本地)" echo "" echo -e "${YELLOW}提示: 如需配置 SSL 证书,请执行:${NC}" echo " ./deploy.sh ssl obtain" echo "" } # 完整部署 (含 SSL) do_deploy_full() { log_info "开始完整部署 (含 SSL 证书)..." # 基础部署 do_deploy docker # 安装并获取 SSL 证书 do_ssl obtain # 配置自动续期 do_ssl auto-renew log_success "完整部署完成 (含 SSL)!" echo "" echo -e "${CYAN}访问地址:${NC}" echo " 用户前端: https://$DOMAIN" echo " 管理后台: https://$DOMAIN/admin" echo "" } #=============================================================================== # SSL 证书管理 (Let's Encrypt) #=============================================================================== # 安装 Certbot install_certbot() { log_step "检查/安装 Certbot..." if command -v certbot &> /dev/null; then log_success "Certbot 已安装" return 0 fi # 检测操作系统 if [ -f /etc/debian_version ]; then # Debian/Ubuntu apt-get update apt-get install -y certbot python3-certbot-nginx elif [ -f /etc/redhat-release ]; then # CentOS/RHEL yum install -y epel-release yum install -y certbot python3-certbot-nginx elif [ -f /etc/alpine-release ]; then # Alpine apk add certbot certbot-nginx else log_error "无法识别的操作系统,请手动安装 certbot" exit 1 fi log_success "Certbot 安装完成" } # 申请 SSL 证书 obtain_ssl_cert() { local domain=${1:-$DOMAIN} local email=${2:-$ADMIN_EMAIL} log_step "申请 SSL 证书: $domain..." # 创建证书目录 mkdir -p "$PROJECT_ROOT/nginx/ssl" # 停止 Nginx (释放 80 端口) docker-compose stop nginx 2>/dev/null || true # 使用 standalone 模式申请证书 certbot certonly \ --standalone \ --non-interactive \ --agree-tos \ --email "$email" \ -d "$domain" \ --cert-path "$PROJECT_ROOT/nginx/ssl/cert.pem" \ --key-path "$PROJECT_ROOT/nginx/ssl/privkey.pem" \ --fullchain-path "$PROJECT_ROOT/nginx/ssl/fullchain.pem" # 复制证书到项目目录 if [ -d "/etc/letsencrypt/live/$domain" ]; then cp "/etc/letsencrypt/live/$domain/fullchain.pem" "$PROJECT_ROOT/nginx/ssl/" cp "/etc/letsencrypt/live/$domain/privkey.pem" "$PROJECT_ROOT/nginx/ssl/" chmod 644 "$PROJECT_ROOT/nginx/ssl/fullchain.pem" chmod 600 "$PROJECT_ROOT/nginx/ssl/privkey.pem" log_success "SSL 证书已获取并复制到 nginx/ssl/" else log_error "证书申请失败" return 1 fi # 重启 Nginx docker-compose up -d nginx log_success "SSL 证书配置完成" } # 续期 SSL 证书 renew_ssl_cert() { log_step "续期 SSL 证书..." certbot renew --quiet # 复制新证书 if [ -d "/etc/letsencrypt/live/$DOMAIN" ]; then cp "/etc/letsencrypt/live/$DOMAIN/fullchain.pem" "$PROJECT_ROOT/nginx/ssl/" cp "/etc/letsencrypt/live/$DOMAIN/privkey.pem" "$PROJECT_ROOT/nginx/ssl/" # 重载 Nginx docker-compose exec nginx nginx -s reload log_success "SSL 证书续期完成" fi } # 配置证书自动续期 setup_ssl_auto_renew() { log_step "配置证书自动续期..." # 创建续期脚本 cat > /etc/cron.d/certbot-renew << EOF # 每天凌晨 3 点检查证书续期 0 3 * * * root certbot renew --quiet && cp /etc/letsencrypt/live/$DOMAIN/*.pem $PROJECT_ROOT/nginx/ssl/ && docker-compose -f $PROJECT_ROOT/docker-compose.yml exec -T nginx nginx -s reload EOF log_success "自动续期已配置 (每天 3:00 检查)" } # SSL 操作入口 do_ssl() { local action=${1:-status} case $action in install) install_certbot ;; obtain|get) install_certbot obtain_ssl_cert "$2" "$3" ;; renew) renew_ssl_cert ;; auto-renew) setup_ssl_auto_renew ;; status) echo -e "${PURPLE}SSL 证书状态:${NC}" if [ -f "$PROJECT_ROOT/nginx/ssl/fullchain.pem" ]; then echo "证书文件: 存在" openssl x509 -in "$PROJECT_ROOT/nginx/ssl/fullchain.pem" -noout -dates 2>/dev/null || echo "无法读取证书信息" else echo "证书文件: 不存在" fi if command -v certbot &> /dev/null; then echo "" echo "Certbot 证书列表:" certbot certificates 2>/dev/null || echo "无证书" fi ;; *) log_error "未知 SSL 操作: $action (可选: install, obtain, renew, auto-renew, status)" exit 1 ;; esac } #=============================================================================== # Kong API Gateway 配置 #=============================================================================== # 配置 Kong 路由 setup_kong_routes() { local kong_admin="${KONG_ADMIN_URL:-http://localhost:8001}" log_step "配置 Kong API Gateway 路由..." # 等待 Kong 就绪 local max_attempts=30 local attempt=1 while [ $attempt -le $max_attempts ]; do if curl -s "$kong_admin" > /dev/null 2>&1; then break fi echo -n "." sleep 2 attempt=$((attempt + 1)) done if [ $attempt -gt $max_attempts ]; then log_error "Kong Admin API 不可用" return 1 fi echo "" log_info "Kong Admin API 就绪" # 创建服务和路由 log_info "创建微服务..." # User Service curl -s -X POST "$kong_admin/services" \ -d "name=user-service" \ -d "url=http://user-service:3001" > /dev/null 2>&1 || true curl -s -X POST "$kong_admin/services/user-service/routes" \ -d "name=user-route" \ -d "paths[]=/v1/users" \ -d "paths[]=/v1/auth" \ -d "strip_path=false" > /dev/null 2>&1 || true # Payment Service curl -s -X POST "$kong_admin/services" \ -d "name=payment-service" \ -d "url=http://payment-service:3002" > /dev/null 2>&1 || true curl -s -X POST "$kong_admin/services/payment-service/routes" \ -d "name=payment-route" \ -d "paths[]=/v1/payments" \ -d "paths[]=/v1/balance" \ -d "strip_path=false" > /dev/null 2>&1 || true # Knowledge Service curl -s -X POST "$kong_admin/services" \ -d "name=knowledge-service" \ -d "url=http://knowledge-service:3003" > /dev/null 2>&1 || true curl -s -X POST "$kong_admin/services/knowledge-service/routes" \ -d "name=knowledge-route" \ -d "paths[]=/v1/knowledge" \ -d "strip_path=false" > /dev/null 2>&1 || true # Conversation Service curl -s -X POST "$kong_admin/services" \ -d "name=conversation-service" \ -d "url=http://conversation-service:3004" > /dev/null 2>&1 || true curl -s -X POST "$kong_admin/services/conversation-service/routes" \ -d "name=conversation-route" \ -d "paths[]=/v1/conversations" \ -d "strip_path=false" > /dev/null 2>&1 || true # Evolution Service (Admin) curl -s -X POST "$kong_admin/services" \ -d "name=evolution-service" \ -d "url=http://evolution-service:3005" > /dev/null 2>&1 || true curl -s -X POST "$kong_admin/services/evolution-service/routes" \ -d "name=evolution-route" \ -d "paths[]=/v1/evolution" \ -d "paths[]=/v1/memory" \ -d "paths[]=/v1/admin" \ -d "strip_path=false" > /dev/null 2>&1 || true log_info "配置全局插件..." # Rate Limiting curl -s -X POST "$kong_admin/plugins" \ -d "name=rate-limiting" \ -d "config.minute=100" \ -d "config.policy=local" > /dev/null 2>&1 || true # CORS curl -s -X POST "$kong_admin/plugins" \ -d "name=cors" \ -d "config.origins=https://$DOMAIN,http://localhost" \ -d "config.methods=GET,POST,PUT,DELETE,OPTIONS,PATCH" \ -d "config.headers=Accept,Authorization,Content-Type,X-User-Id,X-Request-Id" \ -d "config.credentials=true" \ -d "config.max_age=3600" > /dev/null 2>&1 || true # Request ID curl -s -X POST "$kong_admin/plugins" \ -d "name=correlation-id" \ -d "config.header_name=X-Request-Id" \ -d "config.generator=uuid" > /dev/null 2>&1 || true log_success "Kong 路由配置完成" # 显示配置结果 echo "" echo -e "${PURPLE}已配置的服务:${NC}" curl -s "$kong_admin/services" | grep -o '"name":"[^"]*"' | sed 's/"name":"//g;s/"//g' | while read name; do echo " - $name" done echo "" echo -e "${PURPLE}已配置的路由:${NC}" curl -s "$kong_admin/routes" | grep -o '"name":"[^"]*"' | sed 's/"name":"//g;s/"//g' | while read name; do echo " - $name" done } # Kong 操作入口 do_kong() { local action=${1:-status} case $action in setup|init) setup_kong_routes ;; status) local kong_admin="${KONG_ADMIN_URL:-http://localhost:8001}" echo -e "${PURPLE}Kong 状态:${NC}" curl -s "$kong_admin" > /dev/null 2>&1 && echo "Kong Admin API: 运行中" || echo "Kong Admin API: 未运行" curl -s "http://localhost:8000" > /dev/null 2>&1 && echo "Kong Proxy: 运行中" || echo "Kong Proxy: 未运行" echo "" echo "服务数量: $(curl -s "$kong_admin/services" 2>/dev/null | grep -o '"total":[0-9]*' | cut -d: -f2 || echo 0)" echo "路由数量: $(curl -s "$kong_admin/routes" 2>/dev/null | grep -o '"total":[0-9]*' | cut -d: -f2 || echo 0)" ;; services) curl -s "http://localhost:8001/services" | python3 -m json.tool 2>/dev/null || curl -s "http://localhost:8001/services" ;; routes) curl -s "http://localhost:8001/routes" | python3 -m json.tool 2>/dev/null || curl -s "http://localhost:8001/routes" ;; plugins) curl -s "http://localhost:8001/plugins" | python3 -m json.tool 2>/dev/null || curl -s "http://localhost:8001/plugins" ;; *) log_error "未知 Kong 操作: $action (可选: setup, status, services, routes, plugins)" exit 1 ;; esac } #=============================================================================== # 数据库操作 #=============================================================================== do_db() { local action=${1:-status} case $action in migrate) log_step "执行数据库迁移..." # 可以添加 TypeORM 迁移命令 for service in user payment knowledge conversation evolution; do local dir="${SERVICE_DIRS[$service]}" cd "$PROJECT_ROOT/$dir" pnpm run migration:run 2>/dev/null || log_warning "$service 无迁移或迁移失败" cd "$PROJECT_ROOT" done log_success "数据库迁移完成" ;; seed) log_step "初始化种子数据..." # 添加种子数据脚本 log_success "种子数据初始化完成" ;; backup) local backup_dir="$PROJECT_ROOT/backups/$(date +%Y%m%d_%H%M%S)" mkdir -p "$backup_dir" log_step "备份数据库..." docker-compose exec -T postgres pg_dump -U postgres iconsulting > "$backup_dir/postgres.sql" log_success "数据库备份到: $backup_dir" ;; restore) local backup_file=$2 if [ -z "$backup_file" ]; then log_error "请指定备份文件: ./deploy.sh db restore " exit 1 fi log_step "恢复数据库..." docker-compose exec -T postgres psql -U postgres iconsulting < "$backup_file" log_success "数据库恢复完成" ;; reset) log_warning "这将删除所有数据!" read -p "确认继续? (y/N) " confirm if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then docker-compose down -v docker-compose up -d postgres redis neo4j wait_for_service localhost 5432 "PostgreSQL" do_db migrate log_success "数据库已重置" fi ;; status) echo -e "${PURPLE}数据库状态:${NC}" docker-compose exec postgres psql -U postgres -c "SELECT version();" 2>/dev/null || echo "PostgreSQL 未运行" docker-compose exec redis redis-cli ping 2>/dev/null || echo "Redis 未运行" curl -s http://localhost:7474 > /dev/null && echo "Neo4j 运行中" || echo "Neo4j 未运行" ;; *) log_error "未知数据库操作: $action (可选: migrate, seed, backup, restore, reset, status)" exit 1 ;; esac } #=============================================================================== # 帮助信息 #=============================================================================== show_help() { cat << EOF ╔═══════════════════════════════════════════════════════════════════════════════╗ ║ iConsulting 部署管理脚本 ║ ║ 域名: $DOMAIN ║ ╚═══════════════════════════════════════════════════════════════════════════════╝ 用法: ./deploy.sh [target] [options] 命令: build [target] 编译构建 target: all, shared, backend, frontend, conversation, user, payment, knowledge, evolution, web-client, admin-client start [target] [mode] 启动服务 target: all, infra, kong, nginx, backend, conversation, user, payment, knowledge, evolution, postgres, redis, neo4j mode: docker (默认), local stop [target] [mode] 停止服务 (target 同上) restart [target] [mode] 重启服务 (target 同上) status 查看所有服务状态 logs [service] [lines] 查看日志 service: 服务名或 all lines: 显示行数 (默认 100) clean [target] 清理 target: build, deps, docker, logs, all deploy [mode] 完整部署 (构建 + 启动) mode: docker (默认), local deploy-full 完整部署 (含 SSL 证书自动申请) ssl SSL 证书管理 (Let's Encrypt) action: install - 安装 certbot obtain - 申请证书 renew - 续期证书 auto-renew - 配置自动续期 status - 查看证书状态 kong Kong API Gateway 管理 action: setup - 配置路由和插件 status - 查看状态 services - 查看服务列表 routes - 查看路由列表 plugins - 查看插件列表 db 数据库操作 action: migrate, seed, backup, restore, reset, status help 显示此帮助信息 示例: ./deploy.sh deploy # 完整部署 ./deploy.sh deploy-full # 完整部署 (含 SSL) ./deploy.sh ssl obtain # 申请 SSL 证书 ./deploy.sh kong setup # 配置 Kong 路由 ./deploy.sh build conversation # 只构建对话服务 ./deploy.sh start backend local # 本地模式启动所有后端 ./deploy.sh restart user docker # 重启用户服务 (Docker) ./deploy.sh logs conversation 200 # 查看对话服务最近200行日志 ./deploy.sh clean all # 清理所有构建产物和依赖 ./deploy.sh db backup # 备份数据库 ./deploy.sh db migrate # 执行数据库迁移 环境变量: DOMAIN 域名 (默认: iconsulting.szaiai.com) ADMIN_EMAIL 管理员邮箱 (用于 SSL 证书) EOF } #=============================================================================== # 主入口 #=============================================================================== main() { local command=${1:-help} shift || true case $command in build) do_build "$@" ;; start) do_start "$@" ;; stop) do_stop "$@" ;; restart) do_restart "$@" ;; status) do_status ;; logs) do_logs "$@" ;; clean) do_clean "$@" ;; deploy) do_deploy "$@" ;; deploy-full) do_deploy_full ;; ssl) do_ssl "$@" ;; kong) do_kong "$@" ;; db) do_db "$@" ;; help|--help|-h) show_help ;; *) log_error "未知命令: $command" show_help exit 1 ;; esac } # 执行主函数 main "$@"