#!/bin/bash # ============================================================================= # Genex Portal - 部署管理脚本 # ============================================================================= # # 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 配置 # # 默认域名: gogenex.com # 默认邮箱: admin@gogenex.com # # ============================================================================= set -e RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' PROJECT_NAME="genex-portal" IMAGE_NAME="genex-portal" CONTAINER_NAME="genex-portal" DEFAULT_PORT=3001 DEFAULT_DOMAIN="gogenex.com" DEFAULT_EMAIL="admin@gogenex.com" 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"; } check_docker() { if ! command -v docker &> /dev/null; then log_error "Docker 未安装" exit 1 fi if ! docker info &> /dev/null; then log_error "Docker 服务未运行" exit 1 fi } 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 } build() { log_info "构建 Docker 镜像..." $COMPOSE_CMD build --no-cache log_success "镜像构建完成" } start() { log_info "部署 Genex Portal..." 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 "服务部署成功! http://localhost:$PORT" else log_error "服务启动失败,查看日志: ./deploy.sh logs" exit 1 fi } stop() { log_info "停止服务..." $COMPOSE_CMD down log_success "服务已停止" } restart() { 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}}" } 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 + SSL..." if [ ! -f "$conf_file" ]; then log_error "配置文件不存在: $conf_file" exit 1 fi if [ "$EUID" -ne 0 ]; then log_error "需要 root 权限: sudo ./deploy.sh nginx install $domain $email" exit 1 fi # 安装依赖 log_info "[1/5] 检查依赖..." command -v nginx &> /dev/null || { apt update && apt install -y nginx; systemctl enable nginx; systemctl start nginx; } command -v certbot &> /dev/null || apt install -y certbot python3-certbot-nginx # HTTP 临时配置 log_info "[2/5] 部署 HTTP 临时配置..." mkdir -p /var/www/certbot cat > /etc/nginx/sites-available/$domain << HTTPEOF server { listen 80; listen [::]:80; server_name $domain www.$domain; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { proxy_pass http://127.0.0.1:${DEFAULT_PORT}; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; } } HTTPEOF ln -sf /etc/nginx/sites-available/$domain /etc/nginx/sites-enabled/ nginx -t && systemctl reload nginx # SSL 证书 log_info "[3/5] 申请 SSL 证书..." if [ -d "/etc/letsencrypt/live/$domain" ]; then log_warn "证书已存在,跳过" else certbot certonly --webroot --webroot-path=/var/www/certbot \ --email $email --agree-tos --no-eff-email \ -d $domain -d www.$domain log_success "SSL 证书申请成功!" fi # HTTPS 完整配置 log_info "[4/5] 部署 HTTPS 配置..." cp "$conf_file" /etc/nginx/sites-available/$domain nginx -t && systemctl reload nginx # 自动续期 log_info "[5/5] 配置自动续期..." 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 systemctl reload nginx HOOKEOF chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh fi log_success "完成! https://$domain" } cmd_nginx_ssl() { local domain="${1:-$DEFAULT_DOMAIN}" local email="${2:-$DEFAULT_EMAIL}" [ "$EUID" -ne 0 ] && { log_error "需要 root 权限"; exit 1; } mkdir -p /var/www/certbot if [ -d "/etc/letsencrypt/live/$domain" ]; then certbot renew --cert-name $domain else certbot certonly --webroot --webroot-path=/var/www/certbot \ --email $email --agree-tos --no-eff-email \ -d $domain -d www.$domain fi log_success "SSL 证书完成" } cmd_nginx_status() { systemctl is-active nginx &>/dev/null && echo -e "Nginx: ${GREEN}运行中${NC}" || echo -e "Nginx: ${RED}未运行${NC}" command -v certbot &>/dev/null && certbot certificates 2>/dev/null | grep -E "(Certificate Name|Expiry Date|Domains)" | sed 's/^/ /' } cmd_nginx_reload() { [ "$EUID" -ne 0 ] && { log_error "需要 root 权限"; exit 1; } nginx -t && systemctl reload nginx log_success "Nginx 已重载" } show_help() { echo "" echo "Genex Portal 部署脚本" echo "" echo "Docker: build | start | stop | restart | logs | status | clean" echo "Nginx: nginx install [domain] [email] | nginx ssl | nginx status | nginx reload" echo "" echo "默认域名: $DEFAULT_DOMAIN 端口: $DEFAULT_PORT" 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 "未知命令: $2"; exit 1 ;; esac ;; help|--help|-h) show_help ;; *) log_error "未知命令: $1"; show_help; exit 1 ;; esac } main "$@"