#!/bin/bash # 情感博物馆前端部署脚本 # 作者: emotion-museum # 日期: 2025-07-18 # 支持Jenkins CI/CD部署 set -e # 配置变量 - 支持Jenkins环境变量覆盖 REMOTE_HOST="${DEPLOY_HOST:-root@47.111.10.27}" REMOTE_WEB_DIR="${REMOTE_WEB_DIR:-/data/www/emotion-museum}" FRONTEND_DIR="web-flowith" PROJECT_NAME="emotion-museum-frontend" # Jenkins构建信息 BUILD_NUMBER="${BUILD_NUMBER:-manual}" JOB_NAME="${JOB_NAME:-local-deploy}" BUILD_URL="${BUILD_URL:-}" # 颜色输出 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # 日志函数 log_info() { echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" } log_warning() { echo -e "${YELLOW}[WARNING]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" } log_error() { echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" } # 检查远程服务器连接 check_remote_connection() { log_info "检查远程服务器连接..." if ssh -o ConnectTimeout=10 $REMOTE_HOST "echo 'Connection successful'" > /dev/null 2>&1; then log_success "远程服务器连接正常" return 0 else log_error "无法连接到远程服务器 $REMOTE_HOST" return 1 fi } # 检查本地环境 check_local_environment() { log_info "检查本地构建环境..." # 检查Node.js if ! command -v node &> /dev/null; then log_error "Node.js 未安装" return 1 fi # 检查npm if ! command -v npm &> /dev/null; then log_error "npm 未安装" return 1 fi log_info "Node.js 版本: $(node --version)" log_info "npm 版本: $(npm --version)" log_success "本地环境检查通过" return 0 } # 安装依赖 install_dependencies() { log_info "安装前端依赖..." if [ -f "package-lock.json" ]; then npm ci --silent else npm install --silent fi log_success "依赖安装完成" } # 构建前端项目 build_frontend() { log_info "构建前端项目..." # 设置生产环境变量 export NODE_ENV=production export VITE_API_BASE_URL=http://47.111.10.27:19000 # 执行构建 if npm run build; then log_success "前端项目构建成功" # 检查构建产物 if [ -d "dist" ]; then local dist_size=$(du -sh dist | cut -f1) log_info "构建产物大小: $dist_size" log_info "构建产物文件:" ls -la dist/ | head -10 else log_error "构建产物目录不存在" return 1 fi else log_error "前端项目构建失败" return 1 fi } # 创建远程目录 create_remote_directories() { log_info "创建远程目录结构..." ssh $REMOTE_HOST " mkdir -p $REMOTE_WEB_DIR mkdir -p $REMOTE_WEB_DIR/backup mkdir -p /data/logs/nginx " log_success "远程目录创建完成" } # 备份旧版本 backup_old_version() { log_info "备份旧版本..." local backup_name="backup_$(date +%Y%m%d_%H%M%S)" ssh $REMOTE_HOST " if [ -d '$REMOTE_WEB_DIR/$FRONTEND_DIR' ]; then mv '$REMOTE_WEB_DIR/$FRONTEND_DIR' '$REMOTE_WEB_DIR/backup/$backup_name' echo '旧版本已备份到: $REMOTE_WEB_DIR/backup/$backup_name' # 只保留最近5个备份 cd '$REMOTE_WEB_DIR/backup' ls -t | tail -n +6 | xargs -r rm -rf else echo '没有发现旧版本,跳过备份' fi " log_success "备份完成" } # 部署前端文件 deploy_frontend() { log_info "部署前端文件到远程服务器..." # 上传构建产物 if scp -r dist/ "$REMOTE_HOST:$REMOTE_WEB_DIR/$FRONTEND_DIR/"; then log_success "前端文件上传成功" else log_error "前端文件上传失败" return 1 fi # 设置文件权限 ssh $REMOTE_HOST " chown -R www-data:www-data '$REMOTE_WEB_DIR/$FRONTEND_DIR' 2>/dev/null || true chmod -R 755 '$REMOTE_WEB_DIR/$FRONTEND_DIR' " log_success "文件权限设置完成" } # 配置Nginx configure_nginx() { log_info "配置Nginx..." # 创建Nginx配置 ssh $REMOTE_HOST "cat > /etc/nginx/sites-available/emotion-museum << 'EOF' server { listen 80; server_name 47.111.10.27; # 前端静态文件 location /emotion-museum { alias $REMOTE_WEB_DIR/$FRONTEND_DIR; index index.html; try_files \$uri \$uri/ /emotion-museum/index.html; # 静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control \"public, immutable\"; } # HTML文件不缓存 location ~* \.html$ { expires -1; add_header Cache-Control \"no-cache, no-store, must-revalidate\"; } } # API代理到后端网关 location /api/ { proxy_pass http://127.0.0.1:19000/; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; # WebSocket支持 proxy_http_version 1.1; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection \"upgrade\"; } # 日志配置 access_log /data/logs/nginx/emotion-museum-access.log; error_log /data/logs/nginx/emotion-museum-error.log; } EOF" # 启用站点 ssh $REMOTE_HOST " ln -sf /etc/nginx/sites-available/emotion-museum /etc/nginx/sites-enabled/ nginx -t && systemctl reload nginx " 2>/dev/null || log_warning "Nginx配置可能需要手动检查" log_success "Nginx配置完成" } # 健康检查 health_check() { log_info "执行健康检查..." sleep 3 # 检查前端页面 if curl -f -s "http://47.111.10.27/emotion-museum/" > /dev/null 2>&1; then log_success "前端页面访问正常" else log_warning "前端页面访问异常,请检查Nginx配置" fi # 检查API代理 if curl -f -s "http://47.111.10.27/api/user/health" > /dev/null 2>&1; then log_success "API代理正常" else log_warning "API代理异常,请检查后端服务状态" fi } # 显示部署报告 show_deployment_report() { local total_time=$1 echo "" echo "========================================" echo " 前端部署完成报告" echo "========================================" echo "项目名称: $PROJECT_NAME" echo "目标服务器: $REMOTE_HOST" echo "部署路径: $REMOTE_WEB_DIR/$FRONTEND_DIR" echo "部署时间: $(date '+%Y-%m-%d %H:%M:%S')" echo "总耗时: ${total_time}s" if [ "$BUILD_NUMBER" != "manual" ]; then echo "Jenkins构建: #$BUILD_NUMBER" echo "Jenkins任务: $JOB_NAME" [ -n "$BUILD_URL" ] && echo "构建链接: $BUILD_URL" fi echo "========================================" echo "" echo "🌐 访问地址:" echo " 前端页面: http://47.111.10.27/emotion-museum/" echo " API接口: http://47.111.10.27/api/" echo "" echo "📁 远程文件信息:" ssh $REMOTE_HOST " echo '部署目录大小:' du -sh '$REMOTE_WEB_DIR/$FRONTEND_DIR' 2>/dev/null || echo '无法获取目录大小' echo '' echo '主要文件:' ls -la '$REMOTE_WEB_DIR/$FRONTEND_DIR' 2>/dev/null | head -10 || echo '无法列出文件' " echo "" echo "========================================" echo "🎉 前端部署完成!" } # 主函数 main() { local start_time=$(date +%s) log_info "🚀 开始前端部署..." log_info "目标服务器: $REMOTE_HOST" log_info "部署路径: $REMOTE_WEB_DIR/$FRONTEND_DIR" # 检查环境 if ! check_local_environment; then log_error "本地环境检查失败" exit 1 fi if ! check_remote_connection; then log_error "远程服务器连接失败" exit 1 fi # 安装依赖 install_dependencies # 构建项目 build_frontend # 创建远程目录 create_remote_directories # 备份旧版本 backup_old_version # 部署文件 deploy_frontend # 配置Nginx configure_nginx # 健康检查 health_check # 计算总耗时 local end_time=$(date +%s) local total_time=$((end_time - start_time)) # 显示报告 show_deployment_report $total_time log_success "🎉 前端部署完成!" } # 执行主函数 main "$@"