#!/bin/bash #=============================================================================== # 少儿智慧阅读 - 一键部署到宝塔面板 # # 使用前请配置: # 1. 远程服务器信息(IP、用户名、密码/密钥) # 2. 宝塔面板信息(面板地址、API 密钥) # 3. 数据库信息 #=============================================================================== set -e #============================== 配置区域 ============================== # 远程服务器配置 REMOTE_HOST="8.148.151.56" # 服务器 IP REMOTE_USER="root" # SSH 用户名 REMOTE_PORT="22" # SSH 端口 # 推荐使用 SSH 密钥,如无密钥将使用密码认证 REMOTE_PASSWORD="" # SSH 密码(可选) SSH_KEY_FILE="~/.ssh/id_rsa" # SSH 密钥文件 # 部署路径配置 REMOTE_BASE_DIR="/www/wwwroot/reading-platform" # 后端部署目录 REMOTE_FRONTEND_DIR="/www/wwwroot/reading.ycapp.cn" # 前端部署目录(宝塔站点目录) # 宝塔面板配置 BAOTA_PANEL_URL="http://${REMOTE_HOST}:8888" # 宝塔面板地址 BAOTA_API_KEY="" # 宝塔 API 密钥(在宝塔面板 → 设置 → API 获取) BAOTA_SECRET_KEY="" # 宝塔 API 密钥 # 数据库配置(宝塔中创建的数据库) DB_HOST="localhost" DB_PORT="3306" DB_NAME="lesingle-edu-reading-platform" DB_USER="reading_platform" DB_PASSWORD="" # 数据库密码 # JWT 配置(生产环境必须修改) JWT_SECRET="your-production-jwt-secret-change-this" JWT_EXPIRATION="86400000" # Redis 配置 REDIS_HOST="localhost" REDIS_PORT="6379" REDIS_PASSWORD="" # OSS 配置(生产环境) OSS_ACCESS_KEY_ID="LTAI5tKZhPofbThbSzDSiWoK" OSS_ACCESS_KEY_SECRET="FtcsC7oQX3T0NaChaa9FYq2aoysQFM" OSS_BUCKET_NAME="lesingle-kid-course" OSS_ENDPOINT="oss-cn-shenzhen.aliyuncs.com" #============================== 脚本主体 ============================== # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # 日志函数 log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } log_step() { echo -e "${BLUE}[STEP]${NC} $1"; } # 检查配置 check_config() { log_step "检查配置..." if [ -z "$REMOTE_HOST" ]; then log_error "请配置 REMOTE_HOST(服务器 IP)" exit 1 fi # 检查本地是否有 JAR 包 JAR_FILE="lesingle-edu-reading-platform-backend/target/reading-platform-1.0.0.jar" if [ ! -f "$JAR_FILE" ]; then log_warn "未找到 JAR 包,将先执行构建..." BUILD_NEEDED=true fi # 检查前端是否有 dist if [ ! -d "lesingle-edu-reading-platform-frontend/dist" ]; then log_warn "未找到前端构建文件,将先执行构建..." BUILD_FRONTEND_NEEDED=true fi } # 构建后端 build_backend() { if [ "$BUILD_NEEDED" != "true" ]; then return fi log_step "构建后端服务..." cd "lesingle-edu-reading-platform-backend" # 检查 JDK 版本 if command -v java &> /dev/null; then JAVA_VERSION=$(java -version 2>&1 | head -n 1 | cut -d'"' -f2 | cut -d'.' -f1) if [ "$JAVA_VERSION" != "17" ]; then log_warn "当前 Java 版本为 $JAVA_VERSION,建议 JDK 17" fi fi # Maven 构建 if command -v mvn &> /dev/null; then mvn clean package -DskipTests else log_error "未找到 Maven,请先安装" exit 1 fi cd .. if [ -f "$JAR_FILE" ]; then log_info "后端构建成功:$JAR_FILE" else log_error "后端构建失败" exit 1 fi } # 构建前端 build_frontend() { if [ "$BUILD_FRONTEND_NEEDED" != "true" ]; then return fi log_step "构建前端..." cd "reading-platform-frontend" # 检查 Node.js if ! command -v node &> /dev/null; then log_error "未找到 Node.js,请先安装" exit 1 fi # 安装依赖(如果 node_modules 不存在) if [ ! -d "node_modules" ]; then log_info "安装前端依赖..." npm install fi # 构建 npm run build cd .. if [ -d "lesingle-edu-reading-platform-frontend/dist" ]; then log_info "前端构建成功" else log_error "前端构建失败" exit 1 fi } # SSH 上传文件 upload_files() { log_step "上传文件到远程服务器..." # 创建 SSH 命令别名(根据是否有密码) if [ -n "$REMOTE_PASSWORD" ]; then SSH_PASS="sshpass -p '$REMOTE_PASSWORD'" SCP_PASS="sshpass -p '$REMOTE_PASSWORD'" else SSH_PASS="" SCP_PASS="" fi # 测试 SSH 连接 log_info "测试 SSH 连接..." if ! ssh -o StrictHostKeyChecking=no -p "$REMOTE_PORT" "$REMOTE_USER@$REMOTE_HOST" "echo '连接成功'" 2>/dev/null; then log_error "SSH 连接失败,请检查服务器配置" exit 1 fi log_info "SSH 连接成功" # 创建远程目录 log_info "创建远程目录..." ssh -o StrictHostKeyChecking=no -p "$REMOTE_PORT" "$REMOTE_USER@$REMOTE_HOST" << 'ENDSSH' mkdir -p /www/wwwroot/reading-platform mkdir -p /www/wwwroot/reading-platform/logs ENDSSH # 上传 JAR 包 log_info "上传后端 JAR 包..." scp -o StrictHostKeyChecking=no -P "$REMOTE_PORT" \ "lesingle-edu-reading-platform-backend/target/reading-platform-1.0.0.jar" \ "$REMOTE_USER@$REMOTE_HOST:/www/wwwroot/reading-platform/app.jar" # 上传启动脚本 log_info "上传启动脚本..." cat > /tmp/deploy-start.sh << 'EOF' #!/bin/bash # 阅读平台后端启动脚本 APP_DIR="/www/wwwroot/reading-platform" JAR_FILE="$APP_DIR/app.jar" PID_FILE="$APP_DIR/app.pid" LOG_FILE="$APP_DIR/logs/application.log" # 环境变量 export SPRING_PROFILES_ACTIVE=prod export SERVER_PORT=8480 export DB_HOST=${DB_HOST:-localhost} export DB_PASSWORD=${DB_PASSWORD} export JWT_SECRET=${JWT_SECRET} # 检查是否已在运行 if [ -f "$PID_FILE" ]; then OLD_PID=$(cat "$PID_FILE") if kill -0 "$OLD_PID" 2>/dev/null; then echo "停止旧进程 (PID: $OLD_PID)..." kill "$OLD_PID" sleep 3 fi rm -f "$PID_FILE" fi # 启动应用 echo "启动阅读平台后端..." nohup java -jar \ -Xms512m \ -Xmx1024m \ -XX:+UseG1GC \ -XX:MaxGCPauseMillis=200 \ -XX:+HeapDumpOnOutOfMemoryError \ -XX:HeapDumpPath="$APP_DIR/logs/heapdump.hprof" \ "$JAR_FILE" \ --spring.profiles.active=prod \ > "$LOG_FILE" 2>&1 & NEW_PID=$! echo $NEW_PID > "$PID_FILE" echo "应用已启动 (PID: $NEW_PID)" echo "日志文件:$LOG_FILE" # 等待启动 echo "等待应用启动..." for i in {1..60}; do if curl -s http://localhost:8480/actuator/health > /dev/null 2>&1; then echo "应用启动成功!" exit 0 fi sleep 2 done echo "应用启动超时,请检查日志" tail -100 "$LOG_FILE" exit 1 EOF scp -o StrictHostKeyChecking=no -P "$REMOTE_PORT" \ /tmp/deploy-start.sh \ "$REMOTE_USER@$REMOTE_HOST:/www/wwwroot/reading-platform/start.sh" # 上传停止脚本 cat > /tmp/deploy-stop.sh << 'EOF' #!/bin/bash PID_FILE="/www/wwwroot/reading-platform/app.pid" if [ -f "$PID_FILE" ]; then PID=$(cat "$PID_FILE") if kill -0 "$PID" 2>/dev/null; then echo "停止应用 (PID: $PID)..." kill "$PID" sleep 3 fi rm -f "$PID_FILE" fi # 确保进程已停止 pkill -f "reading-platform-1.0.0.jar" 2>/dev/null || true echo "应用已停止" EOF scp -o StrictHostKeyChecking=no -P "$REMOTE_PORT" \ /tmp/deploy-stop.sh \ "$REMOTE_USER@$REMOTE_HOST:/www/wwwroot/reading-platform/stop.sh" # 设置执行权限 ssh -o StrictHostKeyChecking=no -p "$REMOTE_PORT" "$REMOTE_USER@$REMOTE_HOST" \ "chmod +x /www/wwwroot/reading-platform/start.sh /www/wwwroot/reading-platform/stop.sh" log_info "文件上传完成" } # 上传前端文件 upload_frontend() { log_step "上传前端文件..." # 确保前端目录存在 ssh -o StrictHostKeyChecking=no -p "$REMOTE_PORT" "$REMOTE_USER@$REMOTE_HOST" \ "mkdir -p $REMOTE_FRONTEND_DIR" # 上传 dist 目录 log_info "上传前端构建文件..." scp -o StrictHostKeyChecking=no -r -P "$REMOTE_PORT" \ "lesingle-edu-reading-platform-frontend/dist/" \ "$REMOTE_USER@$REMOTE_HOST:$REMOTE_FRONTEND_DIR/" log_info "前端文件上传完成" } # 创建配置文件 create_config() { log_step "创建生产环境配置..." # 创建环境变量文件 cat > /tmp/remote-env << EOF # 数据库配置 DB_HOST=$DB_HOST DB_PASSWORD=$DB_PASSWORD # JWT 配置 JWT_SECRET=$JWT_SECRET JWT_EXPIRATION=$JWT_EXPIRATION # Redis 配置 REDIS_HOST=$REDIS_HOST REDIS_PORT=$REDIS_PORT REDIS_PASSWORD=$REDIS_PASSWORD # OSS 配置 OSS_ACCESS_KEY_ID=$OSS_ACCESS_KEY_ID OSS_ACCESS_KEY_SECRET=$OSS_ACCESS_KEY_SECRET OSS_BUCKET_NAME=$OSS_BUCKET_NAME OSS_ENDPOINT=$OSS_ENDPOINT EOF scp -o StrictHostKeyChecking=no -P "$REMOTE_PORT" \ /tmp/remote-env \ "$REMOTE_USER@$REMOTE_HOST:/www/wwwroot/reading-platform/.env" log_info "配置文件创建完成" } # 远程部署 remote_deploy() { log_step "执行远程部署..." ssh -o StrictHostKeyChecking=no -p "$REMOTE_PORT" "$REMOTE_USER@$REMOTE_HOST" << 'ENDSSH' cd /www/wwwroot/reading-platform # 停止旧服务 echo "停止旧服务..." bash stop.sh # 启动新服务 echo "启动新服务..." bash start.sh ENDSSH log_info "远程部署完成" } # 显示部署信息 show_deploy_info() { echo "" echo "==============================================" echo " 部署完成!" echo "==============================================" echo "" echo "后端 API 地址:http://${REMOTE_HOST}:8480" echo "前端访问地址:http://${REMOTE_HOST}" echo "API 文档地址:http://${REMOTE_HOST}:8480/swagger-ui.html" echo "" echo "远程目录:" echo " 后端:/www/wwwroot/reading-platform" echo " 前端:$REMOTE_FRONTEND_DIR" echo "" echo "管理命令:" echo " ssh $REMOTE_USER@$REMOTE_HOST" echo " cd /www/wwwroot/reading-platform" echo " ./start.sh # 启动" echo " ./stop.sh # 停止" echo " ./restart.sh # 重启" echo "" echo "日志查看:" echo " tail -f /www/wwwroot/reading-platform/logs/application.log" echo "" echo "==============================================" } # 主函数 main() { echo "" echo "==============================================" echo " 少儿智慧阅读" echo " 一键部署到宝塔面板" echo "==============================================" echo "" check_config build_backend build_frontend upload_files upload_frontend create_config remote_deploy show_deploy_info } # 执行 main "$@"