diff --git a/install.sh b/install.sh index 6faf8b6..ed26334 100644 --- a/install.sh +++ b/install.sh @@ -16,6 +16,8 @@ elif [[ "$1" == "--update" ]] || [[ "$1" == "--upgrade" ]] || [[ "$1" == "update MODE="update" elif [[ "$1" == "--repair" ]] || [[ "$1" == "--fix" ]] || [[ "$1" == "repair" ]]; then MODE="repair" +elif [[ "$1" == "--ssl" ]] || [[ "$1" == "--cert" ]] || [[ "$1" == "ssl" ]]; then + MODE="ssl" fi # 颜色定义 @@ -3381,12 +3383,13 @@ main() { echo -e "${GREEN}[1]${NC} 安装/部署 玩玩云" echo -e "${BLUE}[2]${NC} 更新/升级 玩玩云" echo -e "${YELLOW}[3]${NC} 修复/重新配置 玩玩云" + echo -e "${PURPLE}[5]${NC} SSL证书管理(安装/续签/更换证书)" echo -e "${RED}[4]${NC} 卸载 玩玩云" echo -e "${GRAY}[0]${NC} 退出脚本" echo "" while true; do - read -p "请输入选项 [0-4]: " mode_choice < /dev/tty + read -p "请输入选项 [0-5]: " mode_choice < /dev/tty case $mode_choice in 1) print_success "已选择: 安装模式" @@ -3405,6 +3408,12 @@ main() { repair_main exit 0 ;; + 5) + print_info "切换到SSL证书管理模式..." + echo "" + ssl_main + exit 0 + ;; 4) print_info "切换到卸载模式..." echo "" @@ -3430,8 +3439,9 @@ main() { echo -e "${YELLOW}提示:${NC}" echo " 安装: wget https://gitee.com/yu-yon/vue-driven-cloud-storage/raw/master/install.sh && bash install.sh" echo " 更新: wget https://gitee.com/yu-yon/vue-driven-cloud-storage/raw/master/install.sh && bash install.sh --update" - echo " 卸载: wget https://gitee.com/yu-yon/vue-driven-cloud-storage/raw/master/install.sh && bash install.sh --uninstall" echo " 修复: wget https://gitee.com/yu-yon/vue-driven-cloud-storage/raw/master/install.sh && bash install.sh --repair" + echo " SSL管理: wget https://gitee.com/yu-yon/vue-driven-cloud-storage/raw/master/install.sh && bash install.sh --ssl" + echo " 卸载: wget https://gitee.com/yu-yon/vue-driven-cloud-storage/raw/master/install.sh && bash install.sh --uninstall" echo "" sleep 2 fi @@ -3817,6 +3827,463 @@ repair_main() { # 完成提示 print_repair_completion } + +################################################################################ +# SSL证书管理功能 +################################################################################ + +print_ssl_banner() { + clear + echo -e "${GREEN}" + echo "╔═══════════════════════════════════════════════════════════════╗" + echo "║ ║" + echo "║ 🔐 SSL证书管理模式 ║" + echo "║ ║" + echo "║ SSL Certificate Manager ║" + echo "║ ║" + echo "╚═══════════════════════════════════════════════════════════════╝" + echo -e "${NC}" +} + +confirm_ssl_operation() { + print_ssl_banner + + echo -e "${YELLOW}" + echo "本脚本将执行以下操作:" + echo "" + echo "【SSL证书管理】" + echo " ✓ 检测现有域名配置" + echo " ✓ 选择SSL证书部署方案" + echo " ✓ 申请/更换/续签证书" + echo " ✓ 更新Nginx HTTPS配置" + echo " ✓ 重载服务" + echo "" + echo "【将会保留】" + echo " ✓ 数据库文件(用户数据)" + echo " ✓ 用户上传的文件" + echo " ✓ 后端配置文件(.env)" + echo " ✓ 现有HTTP配置" + echo -e "${NC}" + echo "" + + print_info "适用场景: 初次配置HTTPS、更换证书方案、证书续签" + echo "" + + read -p "确定要继续吗? (y/n): " confirm < /dev/tty + + if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then + print_info "已取消操作" + exit 0 + fi + + echo "" +} + +ssl_check_project() { + print_step "检查项目是否已安装..." + + if [[ ! -d "$PROJECT_DIR" ]]; then + print_error "项目未安装: $PROJECT_DIR" + print_info "请先运行安装命令: bash install.sh" + exit 1 + fi + + if [[ ! -f "${PROJECT_DIR}/backend/server.js" ]]; then + print_error "项目目录不完整" + exit 1 + fi + + print_success "项目已安装: $PROJECT_DIR" + echo "" +} + +ssl_load_existing_config() { + print_step "读取现有配置..." + + # 从.env读取后端端口 + if [[ -f "${PROJECT_DIR}/backend/.env" ]]; then + BACKEND_PORT=$(grep "^PORT=" "${PROJECT_DIR}/backend/.env" | cut -d'=' -f2 || echo "40001") + print_success "后端端口: $BACKEND_PORT" + else + BACKEND_PORT="40001" + print_warning ".env文件不存在,使用默认端口: $BACKEND_PORT" + fi + + # 检查现有nginx配置 + local nginx_conf="" + if [[ -f "/etc/nginx/sites-enabled/${PROJECT_NAME}.conf" ]]; then + nginx_conf="/etc/nginx/sites-enabled/${PROJECT_NAME}.conf" + elif [[ -f "/etc/nginx/conf.d/${PROJECT_NAME}.conf" ]]; then + nginx_conf="/etc/nginx/conf.d/${PROJECT_NAME}.conf" + elif [[ -f "/www/server/panel/vhost/nginx/${PROJECT_NAME}.conf" ]]; then + nginx_conf="/www/server/panel/vhost/nginx/${PROJECT_NAME}.conf" + fi + + if [[ -n "$nginx_conf" ]]; then + # 读取HTTP端口 + EXISTING_HTTP_PORT=$(grep "listen" "$nginx_conf" | grep -v "ssl" | grep -v "#" | head -1 | awk '{print $2}' | tr -d ';' || echo "80") + HTTP_PORT=${EXISTING_HTTP_PORT:-80} + + # 检查是否有HTTPS配置 + if grep -q "listen.*ssl" "$nginx_conf"; then + EXISTING_HTTPS_PORT=$(grep "listen.*ssl" "$nginx_conf" | head -1 | awk '{print $2}' | tr -d ';' || echo "443") + HTTPS_PORT=${EXISTING_HTTPS_PORT:-443} + print_info "检测到现有HTTPS配置,端口: $HTTPS_PORT" + else + HTTPS_PORT="443" + print_info "未检测到HTTPS配置,将使用默认端口: 443" + fi + + # 读取域名 + SERVER_NAME=$(grep "server_name" "$nginx_conf" | head -1 | awk '{print $2}' | tr -d ';' || echo "") + if [[ -n "$SERVER_NAME" ]] && [[ "$SERVER_NAME" != "_" ]] && [[ "$SERVER_NAME" != "localhost" ]]; then + DOMAIN="$SERVER_NAME" + USE_DOMAIN=true + print_success "检测到域名: $DOMAIN" + else + USE_DOMAIN=false + print_warning "未检测到域名配置" + fi + + print_success "HTTP端口: $HTTP_PORT" + else + print_error "未找到Nginx配置文件" + exit 1 + fi + + echo "" +} + +ssl_configure_domain() { + print_step "配置域名" + echo "" + + # 如果已有域名,询问是否使用 + if [[ "$USE_DOMAIN" == "true" ]] && [[ -n "$DOMAIN" ]]; then + print_info "检测到现有域名: $DOMAIN" + read -p "是否使用此域名? (y/n): " use_existing < /dev/tty + + if [[ "$use_existing" == "y" || "$use_existing" == "Y" ]]; then + print_success "使用现有域名: $DOMAIN" + echo "" + return 0 + fi + fi + + # 输入新域名 + while true; do + read -p "请输入您的域名 (例如: wwy.example.com): " DOMAIN < /dev/tty + 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" 2>/dev/null | tail -n1 || nslookup "$DOMAIN" 2>/dev/null | grep -A1 "Name:" | tail -1 | awk '{print $2}') + PUBLIC_IP=$(curl -s ifconfig.me || curl -s icanhazip.com || echo "") + + if [[ -n "$DOMAIN_IP" ]] && [[ "$DOMAIN_IP" == "$PUBLIC_IP" ]]; then + print_success "域名已正确解析到当前服务器IP" + USE_DOMAIN=true + break + else + print_warning "域名未解析到当前服务器IP" + print_info "域名解析IP: ${DOMAIN_IP:-未解析}" + print_info "当前服务器IP: $PUBLIC_IP" + read -p "是否继续? (y/n): " continue_choice < /dev/tty + if [[ "$continue_choice" == "y" || "$continue_choice" == "Y" ]]; then + USE_DOMAIN=true + break + fi + fi + done + + echo "" +} + +ssl_choose_method() { + 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配置 (改回HTTP)" + echo -e "${GREEN}[0]${NC} 取消操作" + echo "" + + while true; do + read -p "请输入选项 [0-8]: " ssl_choice < /dev/tty + case $ssl_choice in + 1|2|3|4|5|6|7) + SSL_METHOD=$ssl_choice + break + ;; + 8) + SSL_METHOD=$ssl_choice + print_warning "将移除HTTPS配置,改回HTTP模式" + read -p "确定要继续吗? (y/n): " confirm_remove < /dev/tty + if [[ "$confirm_remove" == "y" || "$confirm_remove" == "Y" ]]; then + break + fi + ;; + 0) + print_info "已取消操作" + exit 0 + ;; + *) + print_error "无效选项,请重新选择" + ;; + esac + done + echo "" +} + +ssl_deploy_certificate() { + print_step "部署SSL证书..." + + # 如果选择移除HTTPS + if [[ "$SSL_METHOD" == "8" ]]; then + print_info "将移除HTTPS配置..." + # 配置为HTTP模式 + configure_nginx_http + return 0 + fi + + # 部署证书 + deploy_ssl + + # 检查证书是否部署成功 + if [[ -f "/etc/nginx/ssl/${DOMAIN}.crt" ]] && [[ -f "/etc/nginx/ssl/${DOMAIN}.key" ]]; then + print_success "证书文件已部署" + else + print_warning "证书文件未找到,将使用HTTP配置" + SSL_METHOD="8" + fi +} + +ssl_update_nginx_config() { + print_step "更新Nginx配置..." + + if [[ "$SSL_METHOD" == "8" ]]; then + # HTTP配置 + configure_nginx_http + else + # HTTPS配置 + configure_nginx_https + fi + + # 测试nginx配置 + if ! nginx -t 2>&1; then + print_error "Nginx配置测试失败" + print_info "请检查配置文件" + return 1 + fi + + print_success "Nginx配置已更新" + echo "" +} + +ssl_reload_services() { + print_step "重载服务..." + + # 重载Nginx + if [[ -d /www/server/nginx ]]; then + # 宝塔面板 + print_info "宝塔环境,重载Nginx..." + if [[ -f /www/server/nginx/sbin/nginx ]]; then + /www/server/nginx/sbin/nginx -s reload 2>/dev/null + if [[ $? -eq 0 ]]; then + print_success "Nginx已重载" + else + /www/server/nginx/sbin/nginx 2>/dev/null + print_success "Nginx已启动" + fi + fi + systemctl reload nginx 2>/dev/null || true + else + # 标准Nginx + systemctl reload nginx + print_success "Nginx已重载" + fi + + # 重启后端服务(更新PUBLIC_PORT配置) + if command -v pm2 &> /dev/null; then + if pm2 list | grep -q "${PROJECT_NAME}-backend"; then + pm2 restart ${PROJECT_NAME}-backend + print_success "后端服务已重启" + fi + fi + + echo "" +} + +ssl_verify_deployment() { + print_step "验证部署..." + + # 检查Nginx + if [[ -d /www/server/nginx ]]; then + if pgrep -x nginx > /dev/null; then + print_success "Nginx运行正常" + else + print_error "Nginx未运行" + fi + else + if systemctl is-active --quiet nginx; then + print_success "Nginx运行正常" + else + print_error "Nginx未运行" + fi + fi + + # 检查SSL证书 + if [[ "$SSL_METHOD" != "8" ]]; then + if [[ -f "/etc/nginx/ssl/${DOMAIN}.crt" ]]; then + print_success "SSL证书已部署: /etc/nginx/ssl/${DOMAIN}.crt" + + # 显示证书信息 + CERT_EXPIRY=$(openssl x509 -enddate -noout -in "/etc/nginx/ssl/${DOMAIN}.crt" 2>/dev/null | cut -d= -f2) + if [[ -n "$CERT_EXPIRY" ]]; then + print_info "证书有效期至: $CERT_EXPIRY" + fi + else + print_warning "SSL证书文件未找到" + fi + fi + + echo "" +} + +print_ssl_completion() { + clear + echo -e "${GREEN}" + echo "╔═══════════════════════════════════════════════════════════════╗" + echo "║ ║" + echo "║ ✓ SSL配置完成! ║" + echo "║ ║" + echo "╚═══════════════════════════════════════════════════════════════╝" + echo -e "${NC}" + echo "" + + # 显示访问地址 + if [[ "$SSL_METHOD" == "8" ]]; then + if [[ "$HTTP_PORT" == "80" ]]; then + echo -e "${CYAN}访问地址:${NC} http://${DOMAIN}" + else + echo -e "${CYAN}访问地址:${NC} http://${DOMAIN}:${HTTP_PORT}" + fi + echo -e "${YELLOW}模式:${NC} HTTP" + else + if [[ "$HTTPS_PORT" == "443" ]]; then + echo -e "${CYAN}访问地址:${NC} https://${DOMAIN}" + else + echo -e "${CYAN}访问地址:${NC} https://${DOMAIN}:${HTTPS_PORT}" + fi + echo -e "${YELLOW}模式:${NC} HTTPS" + + # SSL信息 + echo "" + echo -e "${YELLOW}SSL证书:${NC}" + case $SSL_METHOD in + 1) + echo " 方案: Certbot (Let's Encrypt)" + echo " 续期: 自动续期已配置" + ;; + 2) + echo " 方案: acme.sh + Let's Encrypt" + echo " 续期: 自动续期已配置" + ;; + 3) + echo " 方案: acme.sh + ZeroSSL" + echo " 续期: 自动续期已配置" + ;; + 4) + echo " 方案: acme.sh + Buypass" + echo " 续期: 自动续期已配置" + ;; + 7) + echo " 方案: 手动上传证书" + echo " 续期: 需手动更新证书文件" + ;; + esac + fi + echo "" + + echo -e "${YELLOW}常用命令:${NC}" + echo " 查看证书信息: openssl x509 -text -noout -in /etc/nginx/ssl/${DOMAIN}.crt" + echo " 测试HTTPS: curl -I https://${DOMAIN}" + echo " 查看Nginx日志: tail -f /var/log/nginx/error.log" + echo " 重新配置SSL: bash install.sh --ssl" + echo "" + + echo -e "${GREEN}SSL配置完成!${NC}" + echo "" +} + +ssl_main() { + # 检查root权限 + check_root + + # 检测操作系统 + detect_os + + # 确认操作 + confirm_ssl_operation + + # 检查项目 + ssl_check_project + + # 读取现有配置 + ssl_load_existing_config + + # 配置域名 + if [[ "$USE_DOMAIN" != "true" ]] || [[ -z "$DOMAIN" ]]; then + ssl_configure_domain + fi + + # 选择SSL方案 + ssl_choose_method + + # 先配置基础HTTP Nginx(SSL验证需要) + configure_nginx_http_first + + # 部署SSL证书 + ssl_deploy_certificate + + # 更新Nginx配置 + ssl_update_nginx_config + + # 重载服务 + ssl_reload_services + + # 验证部署 + ssl_verify_deployment + + # 完成提示 + print_ssl_completion +} + # 执行主流程 if [[ "$MODE" == "uninstall" ]]; then uninstall_main @@ -3824,6 +4291,8 @@ elif [[ "$MODE" == "update" ]]; then update_main elif [[ "$MODE" == "repair" ]]; then repair_main +elif [[ "$MODE" == "ssl" ]]; then + ssl_main else main fi