#!/bin/bash # ============================================================================= # 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.cn # 默认邮箱: admin@gogenex.cn # # ============================================================================= set -e # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # 项目信息 PROJECT_NAME="genex-admin-web" IMAGE_NAME="genex-admin-web" CONTAINER_NAME="genex-admin-web" DEFAULT_PORT=3000 DEFAULT_DOMAIN="admin.gogenex.cn" DEFAULT_EMAIL="admin@gogenex.cn" # 脚本目录 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # 日志函数 log_info() { echo -e "${BLUE}[INFO]${NC} $1" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } # ============================================================================= # Docker 环境检查 # ============================================================================= # 检查 Docker 是否安装 check_docker() { if ! command -v docker &> /dev/null; then log_error "Docker 未安装,请先安装 Docker" exit 1 fi if ! docker info &> /dev/null; then log_error "Docker 服务未运行,请启动 Docker" exit 1 fi log_success "Docker 检查通过" } # 检查 Docker Compose 是否安装 check_docker_compose() { if docker compose version &> /dev/null; then COMPOSE_CMD="docker compose" elif command -v docker-compose &> /dev/null; then COMPOSE_CMD="docker-compose" else log_error "Docker Compose 未安装" exit 1 fi log_success "Docker Compose 检查通过 ($COMPOSE_CMD)" } # ============================================================================= # Docker 服务管理 # ============================================================================= # 构建镜像 build() { log_info "开始构建 Docker 镜像..." $COMPOSE_CMD build --no-cache log_success "镜像构建完成" } # 启动服务 start() { log_info "开始部署 Genex Admin Web..." # 检查端口是否被占用 PORT=${PORT:-$DEFAULT_PORT} if lsof -Pi :$PORT -sTCP:LISTEN -t >/dev/null 2>&1; then log_warn "端口 $PORT 已被占用,尝试停止旧服务..." stop fi # 构建并启动 $COMPOSE_CMD up -d --build # 等待服务启动 log_info "等待服务启动..." sleep 5 # 检查服务状态 if docker ps | grep -q $CONTAINER_NAME; then log_success "服务部署成功!" log_info "访问地址: http://localhost:$PORT" log_info "健康检查: http://localhost:$PORT/api/health" else log_error "服务启动失败,请查看日志: ./deploy.sh logs" exit 1 fi } # 停止服务 stop() { log_info "停止服务..." $COMPOSE_CMD down log_success "服务已停止" } # 重启服务 restart() { log_info "重启服务..." stop start } # 查看日志 logs() { $COMPOSE_CMD logs -f } # 清理 clean() { log_info "清理容器和镜像..." # 停止并删除容器 $COMPOSE_CMD down --rmi local --volumes --remove-orphans # 删除悬空镜像 docker image prune -f log_success "清理完成" } # 显示状态 status() { log_info "服务状态:" 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.cn.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 "Docker 命令:" echo " build 仅构建 Docker 镜像" echo " start 构建并启动服务 (默认)" echo " stop 停止服务" echo " restart 重启服务" 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 服务端口 (默认: $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 " 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 "$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 ;; *) log_error "未知命令: $1" show_help exit 1 ;; esac } main "$@"