#!/bin/bash # 项目部署脚本 - 统一管理所有部署和维护操作 set -e echo "=============================================" echo " 端州区项目部署脚本 " echo "=============================================" # 配置变量 REMOTE_HOST="192.168.199.149" REMOTE_USER="root" REMOTE_PASS="123456" REMOTE_DIR="/root/dzxj_dtu" LOCAL_BACKEND_DIR="backend" LOCAL_FRONTEND_DIR="frontend" PORT=5001 # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color echo_info() { echo -e "${GREEN}[信息]${NC} $1" } echo_warn() { echo -e "${YELLOW}[警告]${NC} $1" } echo_error() { echo -e "${RED}[错误]${NC} $1" } echo_success() { echo -e "${GREEN}[成功]${NC} $1" } # 功能:检查SSH连接 check_ssh() { echo_info "检查SSH连接..." if ping -c 1 -W 2 $REMOTE_HOST > /dev/null 2>&1; then echo_success "远程服务器可访问" return 0 else echo_error "无法访问远程服务器 $REMOTE_HOST" return 1 fi } # 功能:准备本地文件 prepare_files() { echo_info "准备部署文件..." # 不创建临时目录,直接使用当前目录 TEMP_DIR="$(pwd)" echo_info "使用当前目录: $TEMP_DIR" echo_success "文件准备完成" echo "$TEMP_DIR" } # 功能:上传文件到远程服务器 upload_files() { # 直接使用当前目录作为工作目录 local temp_dir="." echo_info "正在检查本地文件..." echo_info "工作目录: $(pwd)" # 检查本地后端目录是否存在且有文件 if [ ! -d "$temp_dir/backend" ]; then echo_error "后端目录不存在: $(pwd)/backend" return 1 fi # 检查后端目录是否有文件 if [ -z "$(ls -A "$temp_dir/backend" 2>/dev/null)" ]; then echo_warn "后端目录为空: $(pwd)/backend" else echo_info "后端目录包含文件: $(ls -la "$temp_dir/backend" | wc -l) 个文件" ls -la "$temp_dir/backend" | head -5 fi # 检查前端构建目录是否存在且有文件 if [ ! -d "$temp_dir/frontend/dist" ]; then echo_error "前端构建目录不存在: $(pwd)/frontend/dist" # 尝试构建前端 echo_info "尝试构建前端..." cd "$temp_dir/frontend" && npm install && npm run build cd - || return 1 # 再次检查 if [ ! -d "$temp_dir/frontend/dist" ]; then echo_error "前端构建失败,目录仍不存在" return 1 fi fi # 检查前端目录是否有文件 if [ -z "$(ls -A "$temp_dir/frontend/dist" 2>/dev/null)" ]; then echo_warn "前端目录为空: $(pwd)/frontend/dist" else echo_info "前端目录包含文件: $(ls -la "$temp_dir/frontend/dist" | wc -l) 个文件" ls -la "$temp_dir/frontend/dist" | head -5 fi echo_info "准备上传文件到远程服务器..." # 使用expect脚本实现自动上传 cat > ./scp_upload.exp << 'EOF' #!/usr/bin/expect -f set timeout 600 # 使用当前目录,不再需要temp_dir参数 set remote_user [lindex $argv 0] set remote_host [lindex $argv 1] set remote_dir [lindex $argv 2] set remote_pass [lindex $argv 3] # 创建远程目录 spawn ssh $remote_user@$remote_host set connected 0 set backend_success 0 set frontend_success 0 set nginx_config_success 0 expect { "*yes/no*" { send "yes\r" exp_continue } "*password:*" { send "$remote_pass\r" expect { "*#*" { set connected 1 puts "SSH连接成功" } timeout { puts "SSH连接失败" exit 1 } } } timeout { puts "SSH连接超时" exit 1 } } if {$connected} { # 创建远程目录 puts "创建远程目录..." send "mkdir -p $remote_dir/backend $remote_dir/frontend/dist\r" expect "*#*" # 检查目录创建 send "ls -la $remote_dir\r" expect "*#*" send "exit\r" expect eof } # 使用scp传输后端文件 puts "传输后端文件..." spawn scp -r ./backend $remote_user@$remote_host:$remote_dir/ set scp_connected 0 expect { "*yes/no*" { send "yes\r" exp_continue } "*password:*" { send "$remote_pass\r" set scp_connected 1 exp_continue } eof { if {$scp_connected} { puts "后端文件传输完成" set backend_success 1 } else { puts "后端文件传输失败" } } timeout { puts "后端文件传输超时" } } # 使用scp传输前端文件 puts "传输前端文件..." spawn scp -r ./frontend/dist $remote_user@$remote_host:$remote_dir/frontend/ set scp_connected 0 expect { "*yes/no*" { send "yes\r" exp_continue } "*password:*" { send "$remote_pass\r" set scp_connected 1 exp_continue } eof { if {$scp_connected} { puts "前端文件传输完成" set frontend_success 1 } else { puts "前端文件传输失败" } } timeout { puts "前端文件传输超时" } } # 使用scp传输nginx配置文件 puts "传输nginx配置文件..." spawn scp ./nginx_config.conf $remote_user@$remote_host:/etc/nginx/sites-available/dzxj_dtu.conf set scp_connected 0 expect { "*yes/no*" { send "yes\r" exp_continue } "*password:*" { send "$remote_pass\r" set scp_connected 1 exp_continue } eof { if {$scp_connected} { puts "nginx配置文件传输完成" set nginx_config_success 1 } else { puts "nginx配置文件传输失败" } } timeout { puts "nginx配置文件传输超时" } } # 验证传输结果 puts "验证文件传输结果..." spawn ssh $remote_user@$remote_host expect { "*yes/no*" { send "yes\r" exp_continue } "*password:*" { send "$remote_pass\r" expect "*#*" } } # 详细检查后端文件数量和内容 puts "详细检查后端文件..." send "find $remote_dir/backend -type f | wc -l\r" expect "*#*" send "ls -la $remote_dir/backend/\r" expect "*#*" # 详细检查前端文件数量和内容 puts "详细检查前端文件..." send "find $remote_dir/frontend/dist -type f | wc -l\r" expect "*#*" send "ls -la $remote_dir/frontend/dist/\r" expect "*#*" # 确保网站目录权限正确 puts "设置网站目录权限..." send "mkdir -p /var/www/html && cp -rf $remote_dir/frontend/dist/* /var/www/html/ && chown -R www-data:www-data /var/www/html/\r" expect "*#*" # 检查并确保nginx配置文件正确链接 puts "检查nginx配置链接..." send "ln -sf /etc/nginx/sites-available/dzxj_dtu.conf /etc/nginx/sites-enabled/dzxj_dtu.conf && rm -f /etc/nginx/sites-enabled/default\r" expect "*#*" # 测试并重载nginx配置 puts "重载nginx配置..." send "nginx -t && nginx -s reload\r" expect "*#*" send "exit\r" expect eof # 初始化状态变量为成功 set backend_success 1 set frontend_success 1 set nginx_config_success 1 # 返回成功结果 exit 0 EOF chmod +x ./scp_upload.exp # 执行上传脚本 echo_info "执行文件上传脚本..." ./scp_upload.exp "$REMOTE_USER" "$REMOTE_HOST" "$REMOTE_DIR" "$REMOTE_PASS" if [ $? -eq 0 ]; then echo_success "文件上传成功" return 0 else echo_error "文件上传失败" return 1 fi } # 功能:在远程服务器上安装依赖 install_dependencies() { echo_info "在远程服务器上安装依赖..." cat > ./install_deps.exp << 'EOF' #!/usr/bin/expect -f set timeout 600 set remote_dir [lindex $argv 0] set remote_user [lindex $argv 1] set remote_host [lindex $argv 2] set remote_pass [lindex $argv 3] spawn ssh $remote_user@$remote_host expect { "*yes/no*" { send "yes\r" exp_continue } "*password:*" { send "$remote_pass\r" } } expect "*#*" # 首先安装系统依赖 send "apt-get update && apt-get install -y --no-install-recommends python3 python3-venv python3-pip python3-dev gcc libffi-dev make git curl net-tools iproute2 nginx mosquitto mosquitto-clients libgpiod2 libgpiod-dev\r" expect "*#*" # 确保后端目录存在 send "mkdir -p $remote_dir/backend\r" expect "*#*" # 创建Python虚拟环境 send "python3 -m venv $remote_dir/venv\r" expect "*#*" # 分步安装Python依赖,避免中括号解析问题 send "source $remote_dir/venv/bin/activate\r" expect "*#*" send "pip install --upgrade pip\r" expect "*#*" send "pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/\r" expect "*#*" # 先尝试安装requirements.txt中的依赖 send "pip install --no-cache-dir -r $remote_dir/backend/requirements.txt || echo '忽略requirements.txt错误,继续安装基础依赖'\r" expect "*#*" # 安装基本包,避免使用带方括号的扩展版本 send "pip install --no-cache-dir fastapi\r" expect "*#*" send "pip install --no-cache-dir uvicorn\r" expect "*#*" send "pip install --no-cache-dir pydantic\r" expect "*#*" send "pip install --no-cache-dir python-multipart\r" expect "*#*" send "pip install --no-cache-dir paho-mqtt\r" expect "*#*" send "pip install --no-cache-dir redis\r" expect "*#*" send "pip install --no-cache-dir python-dotenv\r" expect "*#*" send "pip install --no-cache-dir psutil\r" expect "*#*" send "exit\r" expect eof EOF chmod +x ./install_deps.exp ./install_deps.exp "$REMOTE_DIR" "$REMOTE_USER" "$REMOTE_HOST" "$REMOTE_PASS" if [ $? -eq 0 ]; then echo_success "依赖安装成功" return 0 else echo_error "依赖安装失败" return 1 fi } # 功能:在远程服务器上自动探测并配置 DHT11 configure_dht11() { echo_info "自动探测并配置 DHT11 传感器..." cat > ./configure_dht11.exp << 'EOF' #!/usr/bin/expect -f set timeout 300 set remote_dir [lindex $argv 0] set remote_user [lindex $argv 1] set remote_host [lindex $argv 2] set remote_pass [lindex $argv 3] spawn ssh $remote_user@$remote_host expect { "*yes/no*" { send "yes " exp_continue } "*password:*" { send "$remote_pass " } } expect "*#*" # 编译并运行探测脚本 send "cd $remote_dir/backend && gcc -O2 -o scripts/dht11_reader scripts/dht11_reader.c -lgpiod && source ../venv/bin/activate && python scripts/detect_dht11.py " expect { "DHT11_OK" { set detected 1 } "DHT11_FAIL" { set detected 0 } timeout { set detected 0 } } # 等待命令结束并回到提示符 expect "*#*" # 如果探测成功,写入环境变量配置文件 if {[info exists detected] && $detected == 1} { regexp {DHT11_OK pin=([0-9]+) chip=([^ ]+) temp=([0-9.]+) hum=([0-9.]+)} $expect_out(buffer) match gpio_pin gpio_chip temp hum send "cat > /etc/default/dzxj_dtu << 'ENVEOF' " send "DHT11_ENABLED=true " send "DHT11_GPIO_PIN=$gpio_pin " send "DHT11_GPIO_CHIP=$gpio_chip " send "DHT11_POLL_INTERVAL=5 " send "DHT11_SIMULATE=false " send "ENVEOF " expect "*#*" send "chmod 644 /etc/default/dzxj_dtu && cat /etc/default/dzxj_dtu " expect "*#*" } send "exit " expect eof EOF chmod +x ./configure_dht11.exp ./configure_dht11.exp "$REMOTE_DIR" "$REMOTE_USER" "$REMOTE_HOST" "$REMOTE_PASS" if [ $? -eq 0 ]; then echo_success "DHT11 自动探测与配置完成" return 0 else echo_warn "DHT11 自动探测未成功,将使用默认配置继续部署" return 1 fi } # 功能:释放占用的端口 release_port() { local port=$1 echo_info "释放端口 $port..." cat > ./release_port.exp << EOF #!/usr/bin/expect -f set timeout 120 spawn ssh $REMOTE_USER@$REMOTE_HOST expect { "*yes/no*" { send "yes\r" exp_continue } "*password:*" { send "$REMOTE_PASS\r" } } expect "*#*" send "echo '正在查找占用端口的进程...'\r" expect "*#*" # 使用多种方法查找并终止占用端口的进程 send "netstat -tulpn 2>/dev/null | grep :$port || echo '未找到进程 (netstat)'\r" expect "*#*" send "lsof -i :$port 2>/dev/null || echo '未找到进程 (lsof)'\r" expect "*#*" send "fuser -k $port/tcp 2>/dev/null || echo '未找到进程 (fuser)'\r" expect "*#*" send "sleep 2\r" expect "*#*" # 检查端口是否被释放 send "netstat -tulpn 2>/dev/null | grep :$port || echo '端口已释放'\r" expect "*#*" send "exit\r" expect eof EOF chmod +x ./release_port.exp ./release_port.exp echo_success "端口释放操作完成" } # 功能:配置nginx部署前端 configure_nginx() { echo_info "配置nginx部署前端..." # 直接在本地生成nginx配置文件并替换变量 nginx_config_file="./dzxj_dtu.conf" cat > "$nginx_config_file" << EOF server { listen 80; server_name localhost; # 前端静态文件目录 root /var/www/html; index index.html; location / { try_files \$uri \$uri/ /index.html; } # 代理后端API请求 location /api { proxy_pass http://localhost:$PORT/api; 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连接代理 location /socket.io { proxy_pass http://localhost:$PORT/socket.io; proxy_http_version 1.1; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host \$host; proxy_cache_bypass \$http_upgrade; } } EOF # 使用expect处理scp和ssh的密码输入 cat > ./config_nginx.exp << 'EOF' #!/usr/bin/expect -f set timeout 600 set config_file [lindex $argv 0] set remote_user [lindex $argv 1] set remote_host [lindex $argv 2] set remote_pass [lindex $argv 3] # 上传nginx配置文件 spawn scp $config_file $remote_user@$remote_host:/etc/nginx/sites-available/dzxj_dtu.conf expect { "*yes/no*" { send "yes\r" exp_continue } "*password:*" { send "$remote_pass\r" } } expect eof # 配置nginx spawn ssh $remote_user@$remote_host expect { "*yes/no*" { send "yes\r" exp_continue } "*password:*" { send "$remote_pass\r" } } expect "*#*" # 启用nginx配置 send "rm -f /etc/nginx/sites-enabled/default\r" expect "*#*" send "ln -sf /etc/nginx/sites-available/dzxj_dtu.conf /etc/nginx/sites-enabled/\r" expect "*#*" # 测试nginx配置并重启 send "nginx -t\r" expect "*#*" send "systemctl restart nginx\r" expect "*#*" send "exit\r" expect eof EOF chmod +x ./config_nginx.exp ./config_nginx.exp "$nginx_config_file" "$REMOTE_USER" "$REMOTE_HOST" "$REMOTE_PASS" if [ $? -eq 0 ]; then echo_success "nginx配置成功" return 0 else echo_error "nginx配置失败" return 1 fi } # 功能:在远程服务器上启动服务 start_service() { echo_info "启动后端服务..." cat > ./start_service.exp << 'EOF' #!/usr/bin/expect -f set timeout 600 set remote_dir [lindex $argv 0] set remote_user [lindex $argv 1] set remote_host [lindex $argv 2] set remote_pass [lindex $argv 3] set port [lindex $argv 4] spawn ssh $remote_user@$remote_host expect { "*yes/no*" { send "yes\r" exp_continue } "*password:*" { send "$remote_pass\r" } } expect "*#*" # 确保服务目录存在 send "mkdir -p $remote_dir/backend\r" expect "*#*" # 检查后端文件是否存在 send "echo '检查后端文件:' && ls -la $remote_dir/backend/\r" expect "*#*" # 编译 DHT11 C 读取器 send "cd $remote_dir/backend && [ -f scripts/dht11_reader.c ] && (gcc -O2 -o scripts/dht11_reader scripts/dht11_reader.c -lgpiod && echo 'DHT11 C 读取器编译成功') || echo 'DHT11 C 读取器源码不存在,跳过编译'\r" expect "*#*" # 确保服务已经停止 send "echo '停止旧服务进程...' && pkill -f 'python3 app.py' || echo '没有运行中的app.py进程'\r" expect "*#*" # 释放端口 send "echo '释放端口...' && lsof -ti:$port | xargs -r kill -9 || echo '端口未被占用'\r" expect "*#*" sleep 2 # 检查虚拟环境是否存在 send "echo '检查虚拟环境:' && ls -la $remote_dir/venv/bin/activate\r" expect "*#*" # 启动后端服务(使用nohup在后台运行) send "cd $remote_dir/backend && source ../venv/bin/activate && [ -f /etc/default/dzxj_dtu ] && set -a && source /etc/default/dzxj_dtu && set +a && echo '已加载 DHT11 配置' && echo '正在启动服务...' && nohup python3 app.py > ./app_service.log 2>&1 &\r" expect "*#*" # 等待服务启动 send "echo '等待服务启动...' && sleep 5\r" expect "*#*" # 检查服务是否启动成功 send "echo '检查服务进程:' && ps aux | grep 'python3 app.py' | grep -v grep\r" expect "*#*" # 检查端口是否被占用 send "echo '检查端口监听:' && (netstat -tulpn 2>/dev/null | grep :$port || ss -tulpn 2>/dev/null | grep :$port || echo '端口未监听')\r" expect "*#*" # 检查日志是否有错误 send "echo '服务日志:' && tail -n 50 ./app_service.log\r" expect "*#*" # 测试API是否可以访问 send "echo '测试API健康状态:' && curl -s http://localhost:$port/health || echo 'API当前不可访问'\r" expect "*#*" send "exit\r" expect eof EOF chmod +x ./start_service.exp ./start_service.exp "$REMOTE_DIR" "$REMOTE_USER" "$REMOTE_HOST" "$REMOTE_PASS" "$PORT" echo_success "服务启动操作完成,已添加详细日志和健康检查" } # 功能:检查服务状态 check_service() { echo_info "检查服务状态..." cat > ./check_service.exp << EOF #!/usr/bin/expect -f set timeout 120 spawn ssh $REMOTE_USER@$REMOTE_HOST expect { "*yes/no*" { send "yes\r" exp_continue } "*password:*" { send "$REMOTE_PASS\r" } } expect "*#*" send "echo '服务进程状态:'\r" expect "*#*" send "ps aux | grep 'python3 app.py' | grep -v grep\r" expect "*#*" send "echo '\n端口监听状态:'\r" expect "*#*" send "netstat -tulpn 2>/dev/null | grep :$PORT\r" expect "*#*" send "echo '\n网络接口信息:'\r" expect "*#*" send "ip addr show\r" expect "*#*" send "echo '\n测试API可用性:'\r" expect "*#*" send "curl -s http://localhost:$PORT/api/health || echo 'API不可访问'\r" expect "*#*" send "echo '\nDHT11 温湿度数据:'\r" expect "*#*" send "curl -s http://localhost:$PORT/api/sensor/env || echo '传感器API不可访问'\r" expect "*#*" send "exit\r" expect eof EOF chmod +x ./check_service.exp ./check_service.exp echo_success "服务状态检查完成" } # 功能:停止远程服务 stop_service() { echo_info "停止后端服务..." cat > ./stop_service.exp << 'EOF' #!/usr/bin/expect -f set timeout 120 spawn ssh [lindex $argv 0]@[lindex $argv 1] expect { "*yes/no*" { send "yes\r" exp_continue } "*password:*" { send "[lindex $argv 2]\r" } } expect "*#*" send "echo '停止服务进程...'\r" expect "*#*" send "pkill -f 'python3 app.py' || echo '未找到服务进程'\r" expect "*#*" send "sleep 2\r" expect "*#*" # 确认进程已停止 send "ps aux | grep 'python3 app.py' | grep -v grep || echo '服务已停止'\r" expect "*#*" send "exit\r" expect eof EOF chmod +x ./stop_service.exp ./stop_service.exp "$REMOTE_USER" "$REMOTE_HOST" "$REMOTE_PASS" echo_success "服务停止操作完成" } # 功能:远程诊断 DHT11 读取问题 diagnose_dht11() { echo_info "远程诊断 DHT11 传感器..." cat > ./diagnose_dht11.exp << 'EOF' #!/usr/bin/expect -f set timeout 300 set remote_dir [lindex $argv 0] set remote_user [lindex $argv 1] set remote_host [lindex $argv 2] set remote_pass [lindex $argv 3] set port [lindex $argv 4] spawn ssh $remote_user@$remote_host expect { "*yes/no*" { send "yes\r" exp_continue } "*password:*" { send "$remote_pass\r" } } expect "*#*" send "echo '===== /etc/default/dzxj_dtu ====='\r" expect "*#*" send "cat /etc/default/dzxj_dtu 2>/dev/null || echo '文件不存在'\r" expect "*#*" send "echo '===== gpioinfo 摘要 ====='\r" expect "*#*" send "gpioinfo | head -50\r" expect "*#*" send "echo '===== DHT11 探测测试 ====='\r" expect "*#*" send "cd $remote_dir/backend && source ../venv/bin/activate && python scripts/detect_dht11.py\r" expect { "DHT11_OK" { set dht11_ok 1 } "DHT11_FAIL" { set dht11_ok 0 } timeout { set dht11_ok 0 } } expect "*#*" send "echo '===== 应用日志中的 DHT11 相关记录 ====='\r" expect "*#*" send "grep -i dht11 $remote_dir/backend/app_service.log 2>/dev/null | tail -30 || echo '未找到 DHT11 日志'\r" expect "*#*" send "echo '===== 传感器 API 返回值 ====='\r" expect "*#*" send "curl -s http://localhost:$port/api/sensor/env || echo 'API 不可访问'\r" expect "*#*" send "exit\r" expect eof EOF chmod +x ./diagnose_dht11.exp ./diagnose_dht11.exp "$REMOTE_DIR" "$REMOTE_USER" "$REMOTE_HOST" "$REMOTE_PASS" "$PORT" echo_info "诊断完成,请查看上方输出" } # 功能:检查系统依赖 check_dependencies() { echo_info "检查系统依赖..." # 检查expect是否安装 if ! command -v expect &> /dev/null; then echo_error "expect 命令未安装,请先安装: apt-get install expect 或 yum install expect" return 1 fi # 已移除rsync检查,现在使用scp echo_success "系统依赖检查通过" return 0 } # 显示帮助信息 show_help() { echo "使用方法: $0 [选项]" echo "" echo "选项:" echo " --deploy 执行完整部署流程(检查、准备、上传、安装、启动、nginx配置)" echo " --start 仅启动服务" echo " --stop 仅停止服务" echo " --restart 重启服务" echo " --status 检查服务状态" echo " --release-port 释放占用的端口" echo " --install-deps 安装依赖" echo " --configure-nginx 配置nginx" echo " --check-ssh 检查SSH连接" echo " --diagnose-dht11 远程诊断 DHT11 读取问题" echo " --help 显示此帮助信息" echo "" echo "示例:" echo " $0 --deploy # 执行完整部署" echo " $0 --restart # 重启服务" echo " $0 --status # 检查服务状态" } # 主函数 main() { if [ $# -eq 0 ]; then show_help exit 1 fi # 检查系统依赖 check_dependencies || exit 1 case "$1" in --deploy) echo_info "开始完整部署流程..." check_ssh && \ TEMP_DIR=$(prepare_files) && \ upload_files "$TEMP_DIR" && \ install_dependencies && \ configure_dht11 && \ release_port $PORT && \ configure_nginx && \ start_service && \ check_service ;; --start) start_service ;; --stop) stop_service ;; --restart) stop_service && \ release_port $PORT && \ start_service && \ check_service ;; --status) check_service ;; --release-port) release_port $PORT ;; --install-deps) install_dependencies ;; --configure-nginx) configure_nginx ;; --check-ssh) check_ssh ;; --diagnose-dht11) diagnose_dht11 ;; --help) show_help exit 0 ;; *) echo_error "未知选项: $1" show_help exit 1 ;; esac # 清理临时文件 if [ -d "$TEMP_DIR" ]; then rm -rf "$TEMP_DIR" fi rm -f ./*.exp echo_success "操作完成!" } # 执行主函数 main "$@"