From 51f4c60fe5f08cceedcd71c5b94a6871aff4d9a1 Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 11 Feb 2026 01:58:07 -0800 Subject: [PATCH] feat: Add Nginx + Let's Encrypt SSL support to admin-web deploy.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit deploy.sh 新增 nginx 子命令,支持一键安装 Nginx 反向代理和 Let's Encrypt SSL 证书,参考 rwadurian api-gateway 的实现模式。 新增命令: - nginx install [domain] [email]: 完整安装流程 (5步) 1. 自动安装 nginx + certbot 依赖 2. 部署 HTTP 临时配置 (含 ACME 验证路径) 3. certbot webroot 方式申请 SSL 证书 4. 部署 HTTPS 完整配置 (从 nginx/ 目录读取) 5. 配置证书自动续期钩子 (renew hook) - nginx ssl [domain] [email]: 仅申请/续期证书 - nginx status: 查看 Nginx 服务和证书状态 - nginx reload: 重载 Nginx 配置 新增文件: - nginx/admin.gogenex.com.conf: HTTPS 配置模板 含 HTTP→HTTPS 重定向、TLS 1.2/1.3、HSTS、Gzip、 安全头、Next.js 静态资源长缓存、反向代理到 :3000 默认域名: admin.gogenex.com 默认邮箱: admin@gogenex.com 使用方式: 1. ./deploy.sh start # 先启动 Docker 容器 2. sudo ./deploy.sh nginx install # 安装 Nginx + SSL 3. 访问 https://admin.gogenex.com Co-Authored-By: Claude Opus 4.6 --- frontend/admin-web/deploy.sh | 399 +++++++++++++++++- .../admin-web/nginx/admin.gogenex.com.conf | 98 +++++ 2 files changed, 476 insertions(+), 21 deletions(-) create mode 100644 frontend/admin-web/nginx/admin.gogenex.com.conf diff --git a/frontend/admin-web/deploy.sh b/frontend/admin-web/deploy.sh index 25b0de7..ae696a8 100755 --- a/frontend/admin-web/deploy.sh +++ b/frontend/admin-web/deploy.sh @@ -1,15 +1,28 @@ #!/bin/bash -# Genex Admin Web 一键部署脚本 -# 使用方法: ./deploy.sh [命令] -# 命令: -# build - 仅构建镜像 -# start - 构建并启动服务 -# stop - 停止服务 -# restart - 重启服务 -# logs - 查看日志 -# status - 查看服务状态 -# clean - 清理容器和镜像 +# ============================================================================= +# Genex Admin Web - 部署管理脚本 +# ============================================================================= +# +# Docker 命令: +# ./deploy.sh build 仅构建 Docker 镜像 +# ./deploy.sh start 构建并启动服务 (默认) +# ./deploy.sh stop 停止服务 +# ./deploy.sh restart 重启服务 +# ./deploy.sh logs 查看服务日志 +# ./deploy.sh status 查看服务状态 +# ./deploy.sh clean 清理容器和镜像 +# +# Nginx + SSL 命令 (需要 root 权限): +# sudo ./deploy.sh nginx install [domain] [email] 安装 Nginx + SSL 证书 +# sudo ./deploy.sh nginx ssl [domain] [email] 仅申请/续期 SSL 证书 +# ./deploy.sh nginx status 查看 Nginx 状态 +# ./deploy.sh nginx reload 重载 Nginx 配置 +# +# 默认域名: admin.gogenex.com +# 默认邮箱: admin@gogenex.com +# +# ============================================================================= set -e @@ -25,6 +38,11 @@ PROJECT_NAME="genex-admin-web" IMAGE_NAME="genex-admin-web" CONTAINER_NAME="genex-admin-web" DEFAULT_PORT=3000 +DEFAULT_DOMAIN="admin.gogenex.com" +DEFAULT_EMAIL="admin@gogenex.com" + +# 脚本目录 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # 日志函数 log_info() { @@ -43,6 +61,10 @@ log_error() { echo -e "${RED}[ERROR]${NC} $1" } +# ============================================================================= +# Docker 环境检查 +# ============================================================================= + # 检查 Docker 是否安装 check_docker() { if ! command -v docker &> /dev/null; then @@ -72,6 +94,10 @@ check_docker_compose() { log_success "Docker Compose 检查通过 ($COMPOSE_CMD)" } +# ============================================================================= +# Docker 服务管理 +# ============================================================================= + # 构建镜像 build() { log_info "开始构建 Docker 镜像..." @@ -146,14 +172,293 @@ status() { docker ps -a --filter "name=$CONTAINER_NAME" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" } -# 显示帮助 +# ============================================================================= +# Nginx + SSL 证书管理 +# ============================================================================= + +# 安装 Nginx + SSL 证书 (完整流程) +# 流程: +# 1. 安装 nginx 和 certbot (如未安装) +# 2. 部署 HTTP 临时配置 (用于 Let's Encrypt 验证) +# 3. 使用 certbot webroot 方式申请 SSL 证书 +# 4. 部署完整 HTTPS 配置 (从 nginx/ 目录读取) +# 5. 设置 certbot 自动续期定时任务 +cmd_nginx_install() { + local domain="${1:-$DEFAULT_DOMAIN}" + local email="${2:-$DEFAULT_EMAIL}" + local conf_file="$SCRIPT_DIR/nginx/${domain}.conf" + + log_info "为域名 $domain 安装 Nginx + Let's Encrypt SSL..." + + # 检查 HTTPS 配置模板是否存在 + if [ ! -f "$conf_file" ]; then + log_error "Nginx 配置文件不存在: $conf_file" + log_error "请先在 nginx/ 目录下创建 ${domain}.conf" + log_info "" + log_info "可以复制模板并修改:" + log_info " cp nginx/admin.gogenex.com.conf nginx/${domain}.conf" + log_info " 然后替换其中的域名和端口" + exit 1 + fi + + # 检查 root 权限 + if [ "$EUID" -ne 0 ]; then + log_error "需要 root 权限运行" + log_info "用法: sudo ./deploy.sh nginx install $domain $email" + exit 1 + fi + + echo "" + echo "============================================" + echo " Nginx + SSL 安装" + echo "============================================" + echo " 域名: $domain" + echo " 邮箱: $email" + echo " 端口: $DEFAULT_PORT" + echo " 配置: $conf_file" + echo "============================================" + echo "" + + # ---- 步骤 1: 安装依赖 ---- + log_info "[1/5] 检查并安装依赖..." + + if ! command -v nginx &> /dev/null; then + log_info "正在安装 Nginx..." + apt update && apt install -y nginx + systemctl enable nginx + systemctl start nginx + fi + log_success "Nginx 已就绪 ($(nginx -v 2>&1))" + + if ! command -v certbot &> /dev/null; then + log_info "正在安装 Certbot..." + apt install -y certbot python3-certbot-nginx + fi + log_success "Certbot 已就绪 ($(certbot --version 2>&1))" + + # ---- 步骤 2: 部署 HTTP 临时配置 ---- + log_info "[2/5] 部署 HTTP 临时配置 (用于证书验证)..." + + # 创建 certbot webroot 目录 + mkdir -p /var/www/certbot + + # 写入 HTTP-only 临时配置 + cat > /etc/nginx/sites-available/$domain << HTTPEOF +# 临时 HTTP 配置 (用于 Let's Encrypt 证书申请) +# 申请成功后将被 HTTPS 配置替换 +server { + listen 80; + listen [::]:80; + server_name $domain; + + # Let's Encrypt ACME 验证 + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + # 临时反向代理 (证书申请前也可访问) + location / { + proxy_pass http://127.0.0.1:${DEFAULT_PORT}; + proxy_http_version 1.1; + 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; + } +} +HTTPEOF + + # 启用站点配置 + ln -sf /etc/nginx/sites-available/$domain /etc/nginx/sites-enabled/ + + # 移除默认站点 (避免冲突) + rm -f /etc/nginx/sites-enabled/default + + # 测试并重载 Nginx + nginx -t && systemctl reload nginx + log_success "HTTP 配置完成 (http://$domain)" + + # ---- 步骤 3: 申请 SSL 证书 ---- + log_info "[3/5] 申请 Let's Encrypt SSL 证书..." + + if [ -d "/etc/letsencrypt/live/$domain" ]; then + log_warn "证书已存在,跳过申请" + log_info "如需重新申请: sudo certbot certonly --force-renewal -d $domain" + else + echo "" + log_warn "请确保以下条件已满足:" + log_warn " 1. DNS A 记录 $domain 已指向本服务器 IP" + log_warn " 2. 服务器 80 端口可从外网访问 (防火墙已放行)" + echo "" + read -p "确认以上条件已满足,继续申请证书? (y/n): " confirm + if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then + log_info "已跳过证书申请,当前为 HTTP 模式" + log_info "稍后申请证书: sudo ./deploy.sh nginx ssl $domain" + return 0 + fi + + # 使用 webroot 方式申请证书 + certbot certonly --webroot --webroot-path=/var/www/certbot \ + --email $email --agree-tos --no-eff-email \ + -d $domain + + log_success "SSL 证书申请成功!" + fi + + # ---- 步骤 4: 部署 HTTPS 完整配置 ---- + log_info "[4/5] 部署 HTTPS 完整配置..." + + # 从项目 nginx/ 目录复制完整 HTTPS 配置 + cp "$conf_file" /etc/nginx/sites-available/$domain + + # 测试并重载 + nginx -t && systemctl reload nginx + log_success "HTTPS 配置已部署" + + # ---- 步骤 5: 配置自动续期 ---- + log_info "[5/5] 配置证书自动续期..." + + # certbot 安装时通常已自动配置 systemd timer 或 cron + # 这里确保 renew hook 能正确重载 nginx + if [ ! -f /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh ]; then + mkdir -p /etc/letsencrypt/renewal-hooks/deploy + cat > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh << 'HOOKEOF' +#!/bin/bash +# Let's Encrypt 证书续期后自动重载 Nginx +systemctl reload nginx +HOOKEOF + chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh + log_success "自动续期钩子已配置" + else + log_success "自动续期钩子已存在" + fi + + # 测试续期 (dry-run) + certbot renew --dry-run 2>/dev/null && log_success "自动续期测试通过" || log_warn "自动续期测试未通过,请检查 certbot 配置" + + # ---- 完成 ---- + echo "" + echo "============================================" + echo -e " ${GREEN}Nginx + SSL 配置完成!${NC}" + echo "============================================" + echo "" + echo -e " HTTPS 访问: ${BLUE}https://$domain${NC}" + echo -e " HTTP 自动跳转到 HTTPS" + echo "" + echo " Nginx 日志:" + echo " access: /var/log/nginx/${domain}.access.log" + echo " error: /var/log/nginx/${domain}.error.log" + echo "" + echo " 证书路径:" + echo " cert: /etc/letsencrypt/live/$domain/fullchain.pem" + echo " key: /etc/letsencrypt/live/$domain/privkey.pem" + echo "" + echo " 证书续期: certbot 已配置自动续期 (每天检查)" + echo " 手动续期: sudo certbot renew" + echo "" +} + +# 仅申请/续期 SSL 证书 +cmd_nginx_ssl() { + local domain="${1:-$DEFAULT_DOMAIN}" + local email="${2:-$DEFAULT_EMAIL}" + local conf_file="$SCRIPT_DIR/nginx/${domain}.conf" + + # 检查 root 权限 + if [ "$EUID" -ne 0 ]; then + log_error "需要 root 权限运行" + log_info "用法: sudo ./deploy.sh nginx ssl $domain $email" + exit 1 + fi + + if [ -d "/etc/letsencrypt/live/$domain" ]; then + log_info "证书已存在,尝试续期..." + certbot renew --cert-name $domain + log_success "证书续期完成" + else + log_info "为 $domain 申请 SSL 证书..." + + # 确保 webroot 目录存在 + mkdir -p /var/www/certbot + + certbot certonly --webroot --webroot-path=/var/www/certbot \ + --email $email --agree-tos --no-eff-email \ + -d $domain + + log_success "SSL 证书申请成功!" + fi + + # 如果 HTTPS 配置模板存在,部署它 + if [ -f "$conf_file" ]; then + log_info "部署 HTTPS 配置..." + cp "$conf_file" /etc/nginx/sites-available/$domain + nginx -t && systemctl reload nginx + log_success "HTTPS 配置已部署: https://$domain" + else + log_warn "HTTPS 配置模板不存在: $conf_file" + log_warn "请手动配置 Nginx" + fi +} + +# 查看 Nginx 状态 +cmd_nginx_status() { + echo "" + echo "============================================" + echo " Nginx 状态" + echo "============================================" + echo "" + + # Nginx 服务状态 + if systemctl is-active nginx &>/dev/null; then + echo -e " Nginx 服务: ${GREEN}运行中${NC}" + else + echo -e " Nginx 服务: ${RED}未运行${NC}" + fi + + # 域名配置 + echo "" + echo " 已配置站点:" + for conf in /etc/nginx/sites-enabled/*; do + if [ -f "$conf" ]; then + local name=$(basename "$conf") + echo " - $name" + fi + done + + # SSL 证书状态 + echo "" + echo " SSL 证书:" + if command -v certbot &>/dev/null; then + certbot certificates 2>/dev/null | grep -E "(Certificate Name|Expiry Date|Domains)" | sed 's/^/ /' + else + echo " certbot 未安装" + fi + + echo "" +} + +# 重载 Nginx 配置 +cmd_nginx_reload() { + if [ "$EUID" -ne 0 ]; then + log_error "需要 root 权限: sudo ./deploy.sh nginx reload" + exit 1 + fi + + nginx -t && systemctl reload nginx + log_success "Nginx 配置已重载" +} + +# ============================================================================= +# 帮助信息 +# ============================================================================= + show_help() { echo "" echo "Genex Admin Web 部署脚本" echo "" echo "使用方法: ./deploy.sh [命令]" echo "" - echo "命令:" + echo "Docker 命令:" echo " build 仅构建 Docker 镜像" echo " start 构建并启动服务 (默认)" echo " stop 停止服务" @@ -161,49 +466,101 @@ show_help() { echo " logs 查看服务日志" echo " status 查看服务状态" echo " clean 清理容器和镜像" + echo "" + echo "Nginx + SSL 命令:" + echo " nginx install [domain] [email] 安装 Nginx + Let's Encrypt SSL" + echo " nginx ssl [domain] [email] 申请/续期 SSL 证书" + echo " nginx status 查看 Nginx 和证书状态" + echo " nginx reload 重载 Nginx 配置" + echo "" echo " help 显示此帮助信息" echo "" echo "环境变量:" - echo " PORT 服务端口 (默认: 3000)" + echo " PORT 服务端口 (默认: $DEFAULT_PORT)" + echo "" + echo "默认配置:" + echo " 域名: $DEFAULT_DOMAIN" + echo " 邮箱: $DEFAULT_EMAIL" echo "" echo "示例:" - echo " ./deploy.sh start # 默认端口 3000 启动" - echo " PORT=8080 ./deploy.sh start # 指定端口 8080 启动" + echo " ./deploy.sh start # 默认端口 3000 启动" + echo " PORT=8080 ./deploy.sh start # 指定端口 8080 启动" + echo " sudo ./deploy.sh nginx install # 安装 Nginx + SSL (默认域名)" + echo " sudo ./deploy.sh nginx install myapp.com a@b.c # 自定义域名和邮箱" + echo " sudo ./deploy.sh nginx ssl # 续期证书" + echo "" + echo "首次部署完整流程:" + echo " 1. ./deploy.sh start # 启动 Docker 容器" + echo " 2. sudo ./deploy.sh nginx install # 安装 Nginx + SSL" + echo " 3. 访问 https://$DEFAULT_DOMAIN" echo "" } -# 主函数 +# ============================================================================= +# 主入口 +# ============================================================================= + main() { # 切换到脚本所在目录 - cd "$(dirname "$0")" - - # 检查环境 - check_docker - check_docker_compose + cd "$SCRIPT_DIR" # 执行命令 case "${1:-start}" in build) + check_docker + check_docker_compose build ;; start) + check_docker + check_docker_compose start ;; stop) + check_docker + check_docker_compose stop ;; restart) + check_docker + check_docker_compose restart ;; logs) + check_docker + check_docker_compose logs ;; status) + check_docker status ;; clean) + check_docker + check_docker_compose clean ;; + nginx) + case "${2:-install}" in + install) + cmd_nginx_install "$3" "$4" + ;; + ssl) + cmd_nginx_ssl "$3" "$4" + ;; + status) + cmd_nginx_status + ;; + reload) + cmd_nginx_reload + ;; + *) + log_error "未知 nginx 命令: $2" + echo "用法: ./deploy.sh nginx [install|ssl|status|reload] [domain] [email]" + exit 1 + ;; + esac + ;; help|--help|-h) show_help ;; diff --git a/frontend/admin-web/nginx/admin.gogenex.com.conf b/frontend/admin-web/nginx/admin.gogenex.com.conf new file mode 100644 index 0000000..547fc7e --- /dev/null +++ b/frontend/admin-web/nginx/admin.gogenex.com.conf @@ -0,0 +1,98 @@ +# Genex Admin Web - Nginx HTTPS 配置 +# 域名: admin.gogenex.com +# 后端: genex-admin-web (Next.js, 端口 3000) +# +# 此文件由 deploy.sh nginx install 自动部署到: +# /etc/nginx/sites-available/admin.gogenex.com +# +# SSL 证书由 Let's Encrypt (certbot) 自动申请和管理 + +# HTTP 重定向到 HTTPS +server { + listen 80; + listen [::]:80; + server_name admin.gogenex.com; + + # Let's Encrypt 验证目录 + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + # 所有其他请求重定向到 HTTPS + location / { + return 301 https://$host$request_uri; + } +} + +# HTTPS 配置 +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name admin.gogenex.com; + + # SSL 证书 (Let's Encrypt) + ssl_certificate /etc/letsencrypt/live/admin.gogenex.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/admin.gogenex.com/privkey.pem; + + # SSL 配置优化 + ssl_session_timeout 1d; + ssl_session_cache shared:SSL:50m; + ssl_session_tickets off; + + # 现代 TLS 协议与加密套件 + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + + # HSTS (强制 HTTPS, 2年有效) + add_header Strict-Transport-Security "max-age=63072000" always; + + # 日志 + access_log /var/log/nginx/admin.gogenex.com.access.log; + error_log /var/log/nginx/admin.gogenex.com.error.log; + + # Gzip 压缩 + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; + + # 安全响应头 + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + + # Next.js 静态资源 (长期缓存, 文件名含 hash) + location /_next/static/ { + proxy_pass http://127.0.0.1:3000; + proxy_cache_valid 200 365d; + add_header Cache-Control "public, max-age=31536000, immutable"; + } + + # 反向代理到 genex-admin-web (Next.js) + location / { + proxy_pass http://127.0.0.1:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + 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; + proxy_cache_bypass $http_upgrade; + + # 超时配置 + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + # 健康检查 (Nginx 层) + location = /nginx-health { + access_log off; + return 200 '{"status":"ok","service":"genex-admin-nginx"}'; + add_header Content-Type application/json; + } +}