From 4341e82c44025fd3b1a706c2ed185fd9063f98b9 Mon Sep 17 00:00:00 2001 From: WanWanYun Date: Mon, 10 Nov 2025 22:55:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=80=E9=94=AE=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 支持Ubuntu/Debian/CentOS系统自动检测 - 自动安装Node.js、Nginx、PM2等依赖 - 提供官方源和阿里云镜像源选择 - 支持域名/IP两种访问模式 - 6种SSL自动部署方案(Certbot、acme.sh等) - 智能容错和重试机制 - 全程自动化部署,用户仅需选择和输入 --- install.sh | 1105 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1105 insertions(+) create mode 100644 install.sh diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..9218b9c --- /dev/null +++ b/install.sh @@ -0,0 +1,1105 @@ +#!/bin/bash + +################################################################################ +# 玩玩云 (WanWanYun) - 一键部署脚本 +# 项目地址: https://gitee.com/yu-yon/vue-driven-cloud-storage +# 版本: v1.0.0 +################################################################################ + +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' +WHITE='\033[1;37m' +NC='\033[0m' # No Color + +# 全局变量 +PROJECT_NAME="wanwanyun" +PROJECT_DIR="/var/www/${PROJECT_NAME}" +REPO_URL="https://gitee.com/yu-yon/vue-driven-cloud-storage.git" +NODE_VERSION="18" +ADMIN_USERNAME="" +ADMIN_PASSWORD="" +DOMAIN="" +USE_DOMAIN=false +SSL_METHOD="" + +################################################################################ +# 工具函数 +################################################################################ + +print_banner() { + clear + echo -e "${CYAN}" + echo "╔═══════════════════════════════════════════════════════════════╗" + echo "║ ║" + echo "║ 🌩️ 玩玩云 一键部署脚本 ║" + echo "║ ║" + echo "║ Cloud Storage Platform ║" + echo "║ ║" + echo "╚═══════════════════════════════════════════════════════════════╝" + echo -e "${NC}" +} + +print_step() { + echo -e "\n${BLUE}▶ $1${NC}" +} + +print_success() { + echo -e "${GREEN}✓ $1${NC}" +} + +print_error() { + echo -e "${RED}✗ $1${NC}" +} + +print_warning() { + echo -e "${YELLOW}⚠ $1${NC}" +} + +print_info() { + echo -e "${CYAN}ℹ $1${NC}" +} + +# 检测操作系统 +detect_os() { + if [[ -f /etc/os-release ]]; then + . /etc/os-release + OS=$ID + OS_VERSION=$VERSION_ID + else + print_error "无法检测操作系统" + exit 1 + fi +} + +# 检测系统架构 +detect_arch() { + ARCH=$(uname -m) + case $ARCH in + x86_64) + ARCH="amd64" + ;; + aarch64) + ARCH="arm64" + ;; + *) + print_error "不支持的系统架构: $ARCH" + exit 1 + ;; + esac +} + +# 检测root权限 +check_root() { + if [[ $EUID -ne 0 ]]; then + print_error "此脚本需要root权限运行" + print_info "请使用: sudo bash install.sh" + exit 1 + fi +} + +################################################################################ +# 环境检测 +################################################################################ + +system_check() { + print_step "正在检测系统环境..." + + # 检测操作系统 + detect_os + print_success "操作系统: $OS $OS_VERSION" + + # 检测架构 + detect_arch + print_success "系统架构: $ARCH" + + # 检测内存 + TOTAL_MEM=$(free -m | awk '/^Mem:/{print $2}') + if [[ $TOTAL_MEM -lt 512 ]]; then + print_warning "内存不足512MB,可能影响性能" + else + print_success "可用内存: ${TOTAL_MEM}MB" + fi + + # 检测磁盘空间 + DISK_AVAIL=$(df -m / | awk 'NR==2 {print $4}') + if [[ $DISK_AVAIL -lt 2048 ]]; then + print_warning "磁盘空间不足2GB,可能影响运行" + else + print_success "可用磁盘: ${DISK_AVAIL}MB" + fi + + # 检测网络 + if ping -c 1 gitee.com &> /dev/null; then + print_success "网络连接正常" + else + print_error "无法连接到网络" + exit 1 + fi + + # 检测公网IP + PUBLIC_IP=$(curl -s ifconfig.me || curl -s icanhazip.com || echo "未知") + print_info "公网IP: $PUBLIC_IP" + + echo "" +} + +################################################################################ +# 软件源配置 +################################################################################ + +choose_mirror() { + print_step "选择软件包安装源" + echo "" + echo "请选择软件源:" + echo -e "${GREEN}[1]${NC} 官方源 (国外服务器推荐)" + echo -e "${GREEN}[2]${NC} 阿里云镜像源 (国内服务器推荐,速度更快)" + echo "" + + while true; do + read -p "请输入选项 [1-2]: " mirror_choice + case $mirror_choice in + 1) + print_info "使用官方源" + USE_ALIYUN_MIRROR=false + break + ;; + 2) + print_info "使用阿里云镜像源" + USE_ALIYUN_MIRROR=true + configure_aliyun_mirror + break + ;; + *) + print_error "无效选项,请重新选择" + ;; + esac + done + echo "" +} + +configure_aliyun_mirror() { + print_step "配置阿里云镜像源..." + + case $OS in + ubuntu|debian) + # 备份原有源 + if [[ ! -f /etc/apt/sources.list.bak ]]; then + cp /etc/apt/sources.list /etc/apt/sources.list.bak + fi + + # 配置阿里云源 + cat > /etc/apt/sources.list << EOF +deb http://mirrors.aliyun.com/$OS/ $(lsb_release -cs) main restricted universe multiverse +deb http://mirrors.aliyun.com/$OS/ $(lsb_release -cs)-updates main restricted universe multiverse +deb http://mirrors.aliyun.com/$OS/ $(lsb_release -cs)-backports main restricted universe multiverse +deb http://mirrors.aliyun.com/$OS/ $(lsb_release -cs)-security main restricted universe multiverse +EOF + print_success "阿里云源配置完成" + ;; + centos|rhel) + # CentOS配置 + if [[ ! -f /etc/yum.repos.d/CentOS-Base.repo.bak ]]; then + cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak + fi + curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo + yum clean all + yum makecache + print_success "阿里云源配置完成" + ;; + esac +} + +################################################################################ +# 安装依赖环境 +################################################################################ + +install_dependencies() { + print_step "正在安装依赖环境..." + + case $OS in + ubuntu|debian) + apt-get update + apt-get install -y curl wget git unzip + install_nodejs_apt + install_nginx_apt + ;; + centos|rhel) + yum install -y curl wget git unzip + install_nodejs_yum + install_nginx_yum + ;; + esac + + install_pm2 + print_success "依赖环境安装完成" + echo "" +} + +install_nodejs_apt() { + if command -v node &> /dev/null; then + NODE_VER=$(node -v | cut -d'v' -f2 | cut -d'.' -f1) + if [[ $NODE_VER -ge 18 ]]; then + print_success "Node.js 已安装: $(node -v)" + return + fi + fi + + print_info "正在安装 Node.js ${NODE_VERSION}.x..." + curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - + apt-get install -y nodejs + print_success "Node.js 安装完成: $(node -v)" +} + +install_nodejs_yum() { + if command -v node &> /dev/null; then + NODE_VER=$(node -v | cut -d'v' -f2 | cut -d'.' -f1) + if [[ $NODE_VER -ge 18 ]]; then + print_success "Node.js 已安装: $(node -v)" + return + fi + fi + + print_info "正在安装 Node.js ${NODE_VERSION}.x..." + curl -fsSL https://rpm.nodesource.com/setup_${NODE_VERSION}.x | bash - + yum install -y nodejs + print_success "Node.js 安装完成: $(node -v)" +} + +install_nginx_apt() { + if command -v nginx &> /dev/null; then + print_success "Nginx 已安装: $(nginx -v 2>&1 | cut -d'/' -f2)" + return + fi + + print_info "正在安装 Nginx..." + apt-get install -y nginx + systemctl enable nginx + print_success "Nginx 安装完成" +} + +install_nginx_yum() { + if command -v nginx &> /dev/null; then + print_success "Nginx 已安装: $(nginx -v 2>&1 | cut -d'/' -f2)" + return + fi + + print_info "正在安装 Nginx..." + yum install -y nginx + systemctl enable nginx + print_success "Nginx 安装完成" +} + +install_pm2() { + if command -v pm2 &> /dev/null; then + print_success "PM2 已安装: $(pm2 -v)" + return + fi + + print_info "正在安装 PM2..." + npm install -g pm2 + pm2 startup + print_success "PM2 安装完成" +} + +################################################################################ +# 访问模式选择 +################################################################################ + +choose_access_mode() { + print_step "选择访问模式" + echo "" + echo "请选择访问模式:" + echo -e "${GREEN}[1]${NC} 域名模式 (推荐,支持HTTPS)" + echo -e "${GREEN}[2]${NC} IP模式 (仅HTTP,适合测试)" + echo "" + + while true; do + read -p "请输入选项 [1-2]: " mode_choice + case $mode_choice in + 1) + USE_DOMAIN=true + configure_domain + break + ;; + 2) + USE_DOMAIN=false + PUBLIC_IP=$(curl -s ifconfig.me || curl -s icanhazip.com || echo "未知") + print_info "将使用 IP 模式访问: http://${PUBLIC_IP}" + echo "" + break + ;; + *) + print_error "无效选项,请重新选择" + ;; + esac + done +} + +configure_domain() { + echo "" + while true; do + read -p "请输入您的域名 (例如: wwy.example.com): " DOMAIN + if [[ -z "$DOMAIN" ]]; then + print_error "域名不能为空" + continue + fi + + # 验证域名格式 + if [[ ! "$DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$ ]]; then + print_error "域名格式不正确" + continue + fi + + # 验证域名解析 + print_info "正在验证域名解析..." + DOMAIN_IP=$(dig +short "$DOMAIN" | tail -n1) + PUBLIC_IP=$(curl -s ifconfig.me || curl -s icanhazip.com) + + if [[ "$DOMAIN_IP" == "$PUBLIC_IP" ]]; then + print_success "域名已正确解析到当前服务器IP" + break + else + print_warning "域名未解析到当前服务器IP" + print_info "域名解析IP: $DOMAIN_IP" + print_info "当前服务器IP: $PUBLIC_IP" + read -p "是否继续? (y/n): " continue_choice + if [[ "$continue_choice" == "y" || "$continue_choice" == "Y" ]]; then + break + fi + fi + done + + choose_ssl_method +} + +################################################################################ +# SSL证书配置 +################################################################################ + +choose_ssl_method() { + echo "" + print_step "选择SSL证书部署方式" + echo "" + echo -e "${YELLOW}【推荐方案】${NC}" + echo -e "${GREEN}[1]${NC} Certbot (Let's Encrypt官方工具)" + echo " - 最稳定可靠,支持自动续期" + echo "" + echo -e "${YELLOW}【备选方案】${NC}" + echo -e "${GREEN}[2]${NC} acme.sh + Let's Encrypt" + echo " - 纯Shell脚本,更轻量级" + echo -e "${GREEN}[3]${NC} acme.sh + ZeroSSL" + echo " - Let's Encrypt的免费替代品" + echo -e "${GREEN}[4]${NC} acme.sh + Buypass" + echo " - 挪威免费CA,有效期180天" + echo "" + echo -e "${YELLOW}【云服务商证书】${NC}" + echo -e "${GREEN}[5]${NC} 阿里云免费证书 (需提供AccessKey)" + echo -e "${GREEN}[6]${NC} 腾讯云免费证书 (需提供SecretKey)" + echo "" + echo -e "${YELLOW}【其他选项】${NC}" + echo -e "${GREEN}[7]${NC} 使用已有证书 (手动上传)" + echo -e "${GREEN}[8]${NC} 暂不配置HTTPS (可后续配置)" + echo "" + + while true; do + read -p "请输入选项 [1-8]: " ssl_choice + case $ssl_choice in + 1|2|3|4|5|6|7|8) + SSL_METHOD=$ssl_choice + break + ;; + *) + print_error "无效选项,请重新选择" + ;; + esac + done + echo "" +} + +deploy_ssl() { + if [[ "$USE_DOMAIN" != "true" ]]; then + return 0 + fi + + case $SSL_METHOD in + 1) + deploy_certbot || ssl_fallback + ;; + 2) + deploy_acme_letsencrypt || ssl_fallback + ;; + 3) + deploy_acme_zerossl || ssl_fallback + ;; + 4) + deploy_acme_buypass || ssl_fallback + ;; + 5) + deploy_aliyun_ssl || ssl_fallback + ;; + 6) + deploy_tencent_ssl || ssl_fallback + ;; + 7) + deploy_manual_ssl + ;; + 8) + print_info "跳过HTTPS配置" + return 0 + ;; + esac +} + +ssl_fallback() { + print_error "SSL证书部署失败" + echo "" + print_warning "建议尝试备选方案:" + echo -e "${GREEN}[2]${NC} acme.sh + Let's Encrypt (推荐)" + echo -e "${GREEN}[3]${NC} acme.sh + ZeroSSL" + echo -e "${GREEN}[4]${NC} acme.sh + Buypass" + echo -e "${GREEN}[8]${NC} 暂不配置HTTPS" + echo "" + + while true; do + read -p "请选择备选方案 [2-4/8]: " retry_choice + case $retry_choice in + 2) + deploy_acme_letsencrypt && return 0 + ;; + 3) + deploy_acme_zerossl && return 0 + ;; + 4) + deploy_acme_buypass && return 0 + ;; + 8) + print_info "跳过HTTPS配置" + SSL_METHOD=8 + return 0 + ;; + *) + print_error "无效选项" + ;; + esac + done +} + +deploy_certbot() { + print_step "使用 Certbot 部署SSL证书..." + + # 安装certbot + case $OS in + ubuntu|debian) + apt-get install -y certbot python3-certbot-nginx + ;; + centos|rhel) + yum install -y certbot python3-certbot-nginx + ;; + esac + + # 申请证书 + certbot --nginx -d "$DOMAIN" --non-interactive --agree-tos --email "admin@${DOMAIN}" --redirect + + # 配置自动续期 + systemctl enable certbot.timer + + print_success "Certbot SSL证书部署成功" + return 0 +} + +deploy_acme_letsencrypt() { + print_step "使用 acme.sh + Let's Encrypt 部署SSL证书..." + + # 安装acme.sh + if [[ ! -d ~/.acme.sh ]]; then + curl https://get.acme.sh | sh + fi + + # 申请证书 + ~/.acme.sh/acme.sh --issue -d "$DOMAIN" --nginx + + # 安装证书 + mkdir -p /etc/nginx/ssl + ~/.acme.sh/acme.sh --install-cert -d "$DOMAIN" \ + --key-file /etc/nginx/ssl/${DOMAIN}.key \ + --fullchain-file /etc/nginx/ssl/${DOMAIN}.crt \ + --reloadcmd "systemctl reload nginx" + + print_success "acme.sh SSL证书部署成功" + return 0 +} + +deploy_acme_zerossl() { + print_step "使用 acme.sh + ZeroSSL 部署SSL证书..." + + # 安装acme.sh + if [[ ! -d ~/.acme.sh ]]; then + curl https://get.acme.sh | sh + fi + + # 申请证书 + ~/.acme.sh/acme.sh --server zerossl --issue -d "$DOMAIN" --nginx + + # 安装证书 + mkdir -p /etc/nginx/ssl + ~/.acme.sh/acme.sh --install-cert -d "$DOMAIN" \ + --key-file /etc/nginx/ssl/${DOMAIN}.key \ + --fullchain-file /etc/nginx/ssl/${DOMAIN}.crt \ + --reloadcmd "systemctl reload nginx" + + print_success "ZeroSSL证书部署成功" + return 0 +} + +deploy_acme_buypass() { + print_step "使用 acme.sh + Buypass 部署SSL证书..." + + # 安装acme.sh + if [[ ! -d ~/.acme.sh ]]; then + curl https://get.acme.sh | sh + fi + + # 申请证书 + ~/.acme.sh/acme.sh --server buypass --issue -d "$DOMAIN" --nginx + + # 安装证书 + mkdir -p /etc/nginx/ssl + ~/.acme.sh/acme.sh --install-cert -d "$DOMAIN" \ + --key-file /etc/nginx/ssl/${DOMAIN}.key \ + --fullchain-file /etc/nginx/ssl/${DOMAIN}.crt \ + --reloadcmd "systemctl reload nginx" + + print_success "Buypass SSL证书部署成功" + return 0 +} + +deploy_aliyun_ssl() { + print_step "使用阿里云免费证书..." + + print_warning "此功能需要您提供阿里云AccessKey" + echo "" + read -p "阿里云AccessKey ID: " ALIYUN_ACCESS_KEY_ID + read -p "阿里云AccessKey Secret: " ALIYUN_ACCESS_KEY_SECRET + + # 这里需要调用阿里云API申请证书 + # 暂时返回失败,提示用户使用其他方案 + print_error "阿里云证书申请功能开发中,请选择其他方案" + return 1 +} + +deploy_tencent_ssl() { + print_step "使用腾讯云免费证书..." + + print_warning "此功能需要您提供腾讯云SecretKey" + echo "" + read -p "腾讯云SecretId: " TENCENT_SECRET_ID + read -p "腾讯云SecretKey: " TENCENT_SECRET_KEY + + # 这里需要调用腾讯云API申请证书 + # 暂时返回失败,提示用户使用其他方案 + print_error "腾讯云证书申请功能开发中,请选择其他方案" + return 1 +} + +deploy_manual_ssl() { + print_step "使用已有证书..." + + echo "" + print_info "请将以下文件上传到服务器:" + print_info "- 证书文件: /tmp/ssl_cert.crt" + print_info "- 私钥文件: /tmp/ssl_key.key" + echo "" + read -p "上传完成后按回车继续..." + + if [[ -f /tmp/ssl_cert.crt ]] && [[ -f /tmp/ssl_key.key ]]; then + mkdir -p /etc/nginx/ssl + cp /tmp/ssl_cert.crt /etc/nginx/ssl/${DOMAIN}.crt + cp /tmp/ssl_key.key /etc/nginx/ssl/${DOMAIN}.key + chmod 600 /etc/nginx/ssl/${DOMAIN}.key + print_success "证书文件已复制" + return 0 + else + print_error "证书文件未找到" + return 1 + fi +} + +################################################################################ +# 项目部署 +################################################################################ + +create_project_directory() { + print_step "创建项目目录..." + + if [[ -d "$PROJECT_DIR" ]]; then + print_warning "项目目录已存在" + read -p "是否删除并重新创建? (y/n): " recreate + if [[ "$recreate" == "y" || "$recreate" == "Y" ]]; then + rm -rf "$PROJECT_DIR" + else + print_error "部署已取消" + exit 1 + fi + fi + + mkdir -p "$PROJECT_DIR" + print_success "项目目录已创建: $PROJECT_DIR" + echo "" +} + +download_project() { + print_step "正在从Gitee下载项目..." + + cd /tmp + if [[ -d "${PROJECT_NAME}" ]]; then + rm -rf "${PROJECT_NAME}" + fi + + git clone "$REPO_URL" "${PROJECT_NAME}" + + # 复制文件到项目目录 + cp -r "/tmp/${PROJECT_NAME}"/* "$PROJECT_DIR/" + + # 清理临时文件 + rm -rf "/tmp/${PROJECT_NAME}" + + print_success "项目下载完成" + echo "" +} + +configure_admin_account() { + print_step "配置管理员账号" + echo "" + + while true; do + read -p "管理员用户名 [默认: admin]: " ADMIN_USERNAME + ADMIN_USERNAME=${ADMIN_USERNAME:-admin} + + if [[ ${#ADMIN_USERNAME} -lt 3 ]]; then + print_error "用户名至少3个字符" + continue + fi + break + done + + while true; do + read -s -p "管理员密码(至少6位): " ADMIN_PASSWORD + echo "" + if [[ ${#ADMIN_PASSWORD} -lt 6 ]]; then + print_error "密码至少6个字符" + continue + fi + + read -s -p "确认密码: " ADMIN_PASSWORD_CONFIRM + echo "" + if [[ "$ADMIN_PASSWORD" != "$ADMIN_PASSWORD_CONFIRM" ]]; then + print_error "两次密码不一致" + continue + fi + break + done + + print_success "管理员账号配置完成" + echo "" +} + +install_backend_dependencies() { + print_step "安装后端依赖..." + + cd "${PROJECT_DIR}/backend" + + # 使用国内镜像加速 + if [[ "$USE_ALIYUN_MIRROR" == "true" ]]; then + npm config set registry https://registry.npmmirror.com + fi + + npm install --production + + print_success "后端依赖安装完成" + echo "" +} + +create_env_file() { + print_step "创建配置文件..." + + # 生成随机JWT密钥 + JWT_SECRET=$(openssl rand -base64 32) + + cat > "${PROJECT_DIR}/backend/.env" << EOF +# 管理员账号 +ADMIN_USERNAME=${ADMIN_USERNAME} +ADMIN_PASSWORD=${ADMIN_PASSWORD} + +# JWT密钥 +JWT_SECRET=${JWT_SECRET} + +# 数据库路径 +DATABASE_PATH=./data/database.db + +# 存储目录 +STORAGE_ROOT=./storage + +# 服务端口 +PORT=40001 + +# 环境 +NODE_ENV=production +EOF + + print_success "配置文件创建完成" + echo "" +} + +create_data_directories() { + print_step "创建数据目录..." + + mkdir -p "${PROJECT_DIR}/backend/data" + mkdir -p "${PROJECT_DIR}/backend/storage" + + print_success "数据目录创建完成" + echo "" +} + +configure_nginx() { + print_step "配置Nginx..." + + if [[ "$USE_DOMAIN" == "true" ]]; then + if [[ "$SSL_METHOD" == "8" ]]; then + # HTTP配置 + configure_nginx_http + else + # HTTPS配置 + configure_nginx_https + fi + else + # IP模式HTTP配置 + configure_nginx_http + fi + + # 测试nginx配置 + nginx -t + + # 重启nginx + systemctl restart nginx + + print_success "Nginx配置完成" + echo "" +} + +configure_nginx_http() { + local server_name="${DOMAIN:-_}" + + cat > /etc/nginx/sites-available/${PROJECT_NAME}.conf << EOF +server { + listen 80; + server_name ${server_name}; + + # 前端静态文件 + location / { + root ${PROJECT_DIR}/frontend; + index app.html; + try_files \$uri \$uri/ /app.html; + } + + # 后端API + location /api { + proxy_pass http://localhost:40001; + 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; + 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; + } + + # 分享页面 + location /s { + alias ${PROJECT_DIR}/frontend; + try_files /share.html =404; + } + + # 静态资源 + location /libs { + alias ${PROJECT_DIR}/frontend/libs; + expires 30d; + } + + # 上传工具下载 + location /download-tool { + alias ${PROJECT_DIR}/upload-tool/dist; + } +} +EOF + + # 创建软链接 + ln -sf /etc/nginx/sites-available/${PROJECT_NAME}.conf /etc/nginx/sites-enabled/${PROJECT_NAME}.conf + + # 删除默认站点 + rm -f /etc/nginx/sites-enabled/default +} + +configure_nginx_https() { + cat > /etc/nginx/sites-available/${PROJECT_NAME}.conf << EOF +server { + listen 80; + server_name ${DOMAIN}; + return 301 https://\$server_name\$request_uri; +} + +server { + listen 443 ssl http2; + server_name ${DOMAIN}; + + ssl_certificate /etc/nginx/ssl/${DOMAIN}.crt; + ssl_certificate_key /etc/nginx/ssl/${DOMAIN}.key; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + # 前端静态文件 + location / { + root ${PROJECT_DIR}/frontend; + index app.html; + try_files \$uri \$uri/ /app.html; + } + + # 后端API + location /api { + proxy_pass http://localhost:40001; + 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; + 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; + } + + # 分享页面 + location /s { + alias ${PROJECT_DIR}/frontend; + try_files /share.html =404; + } + + # 静态资源 + location /libs { + alias ${PROJECT_DIR}/frontend/libs; + expires 30d; + } + + # 上传工具下载 + location /download-tool { + alias ${PROJECT_DIR}/upload-tool/dist; + } +} +EOF + + # 创建软链接 + ln -sf /etc/nginx/sites-available/${PROJECT_NAME}.conf /etc/nginx/sites-enabled/${PROJECT_NAME}.conf + + # 删除默认站点 + rm -f /etc/nginx/sites-enabled/default +} + +start_backend_service() { + print_step "启动后端服务..." + + cd "${PROJECT_DIR}/backend" + + # 使用PM2启动 + pm2 start server.js --name ${PROJECT_NAME}-backend + pm2 save + + print_success "后端服务已启动" + echo "" +} + +################################################################################ +# 健康检查 +################################################################################ + +health_check() { + print_step "正在进行健康检查..." + + sleep 3 + + # 检查后端服务 + if pm2 status | grep -q "${PROJECT_NAME}-backend.*online"; then + print_success "后端服务运行正常" + else + print_error "后端服务启动失败" + print_info "查看日志: pm2 logs ${PROJECT_NAME}-backend" + return 1 + fi + + # 检查端口 + if netstat -tunlp | grep -q ":40001"; then + print_success "后端端口监听正常 (40001)" + else + print_error "后端端口监听异常" + return 1 + fi + + # 检查Nginx + if systemctl is-active --quiet nginx; then + print_success "Nginx服务运行正常" + else + print_error "Nginx服务异常" + return 1 + fi + + # 检查数据库 + if [[ -f "${PROJECT_DIR}/backend/data/database.db" ]]; then + print_success "数据库初始化成功" + else + print_warning "数据库文件不存在" + fi + + # 检查存储目录 + if [[ -d "${PROJECT_DIR}/backend/storage" ]]; then + print_success "文件存储目录就绪" + else + print_warning "存储目录不存在" + fi + + echo "" + return 0 +} + +################################################################################ +# 完成提示 +################################################################################ + +print_completion() { + clear + echo -e "${GREEN}" + echo "╔═══════════════════════════════════════════════════════════════╗" + echo "║ ║" + echo "║ 🎉 部署成功! ║" + echo "║ ║" + echo "╚═══════════════════════════════════════════════════════════════╝" + echo -e "${NC}" + echo "" + + # 访问地址 + if [[ "$USE_DOMAIN" == "true" ]]; then + if [[ "$SSL_METHOD" == "8" ]]; then + echo -e "${CYAN}访问地址:${NC} http://${DOMAIN}" + else + echo -e "${CYAN}访问地址:${NC} https://${DOMAIN}" + fi + else + PUBLIC_IP=$(curl -s ifconfig.me || curl -s icanhazip.com || echo "服务器IP") + echo -e "${CYAN}访问地址:${NC} http://${PUBLIC_IP}" + fi + + echo -e "${CYAN}管理员账号:${NC} ${ADMIN_USERNAME}" + echo -e "${CYAN}管理员密码:${NC} ********" + echo "" + + # 常用命令 + echo -e "${YELLOW}常用命令:${NC}" + echo " 查看服务状态: pm2 status" + echo " 查看日志: pm2 logs ${PROJECT_NAME}-backend" + echo " 重启服务: pm2 restart ${PROJECT_NAME}-backend" + echo " 停止服务: pm2 stop ${PROJECT_NAME}-backend" + echo "" + + # 配置文件位置 + echo -e "${YELLOW}配置文件位置:${NC}" + echo " 后端配置: ${PROJECT_DIR}/backend/.env" + echo " Nginx配置: /etc/nginx/sites-enabled/${PROJECT_NAME}.conf" + echo " 数据库: ${PROJECT_DIR}/backend/data/database.db" + echo " 文件存储: ${PROJECT_DIR}/backend/storage" + echo "" + + # SSL续期提示 + if [[ "$USE_DOMAIN" == "true" ]] && [[ "$SSL_METHOD" != "8" ]]; then + echo -e "${YELLOW}SSL证书:${NC}" + case $SSL_METHOD in + 1) + echo " 自动续期: 已配置Certbot自动续期" + ;; + 2|3|4) + echo " 自动续期: 已配置acme.sh自动续期" + ;; + esac + echo "" + fi + + echo -e "${GREEN}祝您使用愉快!${NC}" + echo "" +} + +################################################################################ +# 主流程 +################################################################################ + +main() { + print_banner + + # 检查root权限 + check_root + + # 系统检测 + system_check + + # 选择软件源 + choose_mirror + + # 安装依赖 + install_dependencies + + # 选择访问模式 + choose_access_mode + + # 配置管理员账号 + configure_admin_account + + # 创建项目目录 + create_project_directory + + # 下载项目 + download_project + + # 安装后端依赖 + install_backend_dependencies + + # 创建配置文件 + create_env_file + + # 创建数据目录 + create_data_directories + + # 部署SSL证书 + deploy_ssl + + # 配置Nginx + configure_nginx + + # 启动后端服务 + start_backend_service + + # 健康检查 + if ! health_check; then + print_error "健康检查未通过,请检查日志" + exit 1 + fi + + # 完成提示 + print_completion +} + +# 执行主流程 +main