#!/bin/bash # ================================================================ # Nexus 6.0 — One-Click Install Script # # Usage: # curl -fsSL https://YOUR_DOMAIN/deploy/install.sh | sudo bash -s -- --domain nexus.example.com # sudo bash install.sh --domain nexus.example.com # # Supports: Ubuntu 20.04+, Debian 11+, CentOS 7/8, Rocky 8/9, AlmaLinux 8/9 # Auto-detects BT Panel (宝塔面板) and adapts paths accordingly. # ================================================================ set -euo pipefail # ── Colors ── RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' info() { echo -e "${GREEN}[INFO]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*"; } # ── Helper: Idempotent config write ── write_config() { local target="$1" content="$2" if [ -f "$target" ]; then local existing existing=$(cat "$target" 2>/dev/null || true) if [ "$existing" = "$content" ]; then info " [skip] $target (unchanged)" return 0 fi info " [update] $target (content changed)" else info " [create] $target" fi echo "$content" > "$target" } # ── Defaults ── DOMAIN="" DEPLOY_DIR="" PORT=8600 SKIP_NGINX=false SKIP_SUPERVISOR=false BT_MODE=false NEXUS_REPO="" # ── Parse Arguments ── while [[ $# -gt 0 ]]; do case $1 in --domain) DOMAIN="$2"; shift 2 ;; --deploy-dir) DEPLOY_DIR="$2"; shift 2 ;; --port) PORT="$2"; shift 2 ;; --skip-nginx) SKIP_NGINX=true; shift ;; --skip-supervisor) SKIP_SUPERVISOR=true; shift ;; --repo) NEXUS_REPO="$2"; shift 2 ;; -h|--help) echo "Usage: sudo bash install.sh --domain DOMAIN [options]" echo "" echo "Options:" echo " --domain DOMAIN Required. Domain or IP for Nginx server_name" echo " --deploy-dir PATH Installation directory (auto-detected)" echo " --port PORT uvicorn port (default: 8600)" echo " --skip-nginx Skip Nginx configuration" echo " --skip-supervisor Skip Supervisor configuration" echo " --repo URL Git repository URL (optional, for cloning)" exit 0 ;; *) shift ;; esac done # ── Banner ── echo "" echo "==========================================" echo " Nexus 6.0 Install Script" echo "==========================================" echo "" # ================================================================ # Phase 1: Pre-flight Checks # ================================================================ info "Phase 1/7: Pre-flight checks..." # 1.1 Must run as root if [ "$(id -u)" -ne 0 ]; then error "This script must be run as root. Use: sudo bash install.sh" exit 1 fi # 1.2 Domain is required if [ -z "$DOMAIN" ]; then error "--domain is required. Usage: sudo bash install.sh --domain nexus.example.com" exit 1 fi # 1.3 OS detection if [ ! -f /etc/os-release ]; then error "Cannot detect OS. /etc/os-release not found." exit 1 fi OS_ID=$(grep '^ID=' /etc/os-release | head -1 | cut -d'=' -f2 | tr -d '"' | cut -d'-' -f1) OS_VERSION=$(grep '^VERSION_ID=' /etc/os-release | head -1 | cut -d'=' -f2 | tr -d '"') case "$OS_ID" in ubuntu|debian) PKG_MANAGER="apt" ;; centos|rocky|almalinux|rhel) PKG_MANAGER="yum" ;; *) warn "Unsupported OS: $OS_ID. Script may still work but is untested." PKG_MANAGER="apt" ;; esac info " OS: $OS_ID $OS_VERSION (pkg: $PKG_MANAGER)" # 1.4 BT Panel detection if [ -d "/www/server/panel" ] && [ -f "/www/server/panel/class/common.py" ]; then BT_MODE=true info " BT Panel: detected" else info " BT Panel: not detected (standard mode)" fi # 1.5 Set deploy directory if [ -z "$DEPLOY_DIR" ]; then if [ "$BT_MODE" = true ]; then DEPLOY_DIR="/www/wwwroot/$DOMAIN" else DEPLOY_DIR="/opt/nexus" fi fi VENV_DIR="$DEPLOY_DIR/venv" # Set log paths early (used in multiple phases) if [ "$BT_MODE" = true ]; then LOG_DIR="/www/wwwlogs" else LOG_DIR="/var/log/nexus" fi info " Deploy dir: $DEPLOY_DIR" info " Domain: $DOMAIN" info " Port: $PORT" # ================================================================ # Phase 2: System Dependencies # ================================================================ info "Phase 2/7: Installing system dependencies..." if [ "$PKG_MANAGER" = "apt" ]; then export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y -qq \ python3 python3-venv python3-pip python3-dev \ build-essential libssl-dev libffi-dev default-libmysqlclient-dev \ git curl >/dev/null 2>&1 || warn "Some apt packages failed to install (may already exist)" elif [ "$PKG_MANAGER" = "yum" ]; then if command -v dnf >/dev/null 2>&1; then dnf install -y -q \ python3 python3-devel python3-pip \ gcc openssl-devel libffi-devel mysql-devel \ git curl >/dev/null 2>&1 || warn "Some dnf packages failed to install" else yum install -y -q \ python3 python3-devel python3-pip \ gcc openssl-devel libffi-devel mysql-devel \ git curl >/dev/null 2>&1 || warn "Some yum packages failed to install" fi fi # ================================================================ # Phase 3: Python 3.10+ Resolution # ================================================================ info "Phase 3/7: Resolving Python 3.10+..." PYTHON="" CANDIDATES="python3.12 python3.11 python3.10 /usr/bin/python3.12 /usr/bin/python3.11 /usr/bin/python3.10 python3" # Add BT Panel Python paths if [ "$BT_MODE" = true ]; then for p in /www/server/python-manager/versions/*/bin/python3.*; do [ -x "$p" ] && CANDIDATES="$CANDIDATES $p" done fi for candidate in $CANDIDATES; do if command -v "$candidate" >/dev/null 2>&1 || [ -x "$candidate" ]; then ver=$("$candidate" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' 2>/dev/null || true) if [ -n "$ver" ]; then major=$(echo "$ver" | cut -d'.' -f1) minor=$(echo "$ver" | cut -d'.' -f2) if [ "$major" -eq 3 ] && [ "${minor:-0}" -ge 10 ] 2>/dev/null; then PYTHON="$candidate" break fi fi fi done # Fallback: install Python 3.12 on Ubuntu via deadsnakes PPA if [ -z "$PYTHON" ] && [ "$OS_ID" = "ubuntu" ]; then warn " Python 3.10+ not found. Adding deadsnakes PPA..." export DEBIAN_FRONTEND=noninteractive apt-get install -y -qq software-properties-common >/dev/null 2>&1 add-apt-repository -y ppa:deadsnakes/ppa >/dev/null 2>&1 apt-get update -qq apt-get install -y -qq python3.12 python3.12-venv python3.12-dev >/dev/null 2>&1 if command -v python3.12 >/dev/null 2>&1; then PYTHON="python3.12" fi fi if [ -z "$PYTHON" ]; then error "Python 3.10+ is required but not found." error "Install Python 3.10+ manually and re-run." exit 1 fi PYTHON_VERSION=$("$PYTHON" --version 2>&1) info " Using: $PYTHON ($PYTHON_VERSION)" # ================================================================ # Phase 4: Application Deployment # ================================================================ info "Phase 4/7: Deploying application..." # 4.1 Create deploy directory mkdir -p "$DEPLOY_DIR" # 4.2 Download / Clone Nexus if [ -d "$DEPLOY_DIR/server" ] && [ -d "$DEPLOY_DIR/web" ]; then info " Application files already exist in $DEPLOY_DIR (skipping download)" if [ -d "$DEPLOY_DIR/.git" ]; then info " Git repo detected, pulling latest..." (cd "$DEPLOY_DIR" && git pull --ff-only 2>/dev/null || warn " git pull failed (may have local changes)") fi elif [ -n "$NEXUS_REPO" ]; then info " Cloning from $NEXUS_REPO..." git clone "$NEXUS_REPO" "$DEPLOY_DIR" 2>/dev/null || { error "git clone failed. Check the repository URL." exit 1 } else error "No application files found in $DEPLOY_DIR and no --repo URL provided." error "Either:" error " 1. Copy Nexus files to $DEPLOY_DIR manually and re-run" error " 2. Use --repo https://your-gitea-url/Nexus.git" exit 1 fi # 4.3 Create Python venv if [ -d "$VENV_DIR/bin" ] && "$VENV_DIR/bin/python" -c 'import sys; v=sys.version_info; assert v.major==3 and v.minor>=10' 2>/dev/null; then info " venv already exists with Python 3.10+ (skipping)" else info " Creating Python venv..." rm -rf "$VENV_DIR" "$PYTHON" -m venv "$VENV_DIR" fi # 4.4 Install pip dependencies info " Installing Python dependencies (this may take a few minutes)..." "$VENV_DIR/bin/pip" install -q --upgrade pip "$VENV_DIR/bin/pip" install -q -r "$DEPLOY_DIR/requirements.txt" 2>/dev/null || { warn " pip install had some warnings, but continuing..." } # 4.5 Create log directory mkdir -p "$LOG_DIR" # 4.6 Create backup directory mkdir -p "$DEPLOY_DIR/backups" # ================================================================ # Phase 5: Supervisor Configuration # ================================================================ if [ "$SKIP_SUPERVISOR" = true ]; then warn "Phase 5/7: Skipping Supervisor (per --skip-supervisor)" else info "Phase 5/7: Configuring Supervisor..." # Calculate worker count WORKERS=$(nproc 2>/dev/null || echo 2) [ "$WORKERS" -gt 4 ] && WORKERS=4 [ "$WORKERS" -lt 1 ] && WORKERS=1 # Set Supervisor config path if [ "$BT_MODE" = true ]; then SUPERVISOR_CONF_DIR="/www/server/panel/plugin/supervisor/profile" CONF_FILE="$SUPERVISOR_CONF_DIR/nexus.ini" else SUPERVISOR_CONF_DIR="/etc/supervisor/conf.d" CONF_FILE="$SUPERVISOR_CONF_DIR/nexus.conf" fi # Generate Supervisor config SUPERVISOR_CONF="[program:nexus] command=$VENV_DIR/bin/uvicorn server.main:app --host 0.0.0.0 --port $PORT --workers $WORKERS --loop uvloop --http httptools --log-level info directory=$DEPLOY_DIR user=root autostart=true autorestart=true startretries=10 startsecs=3 stopwaitsecs=10 stopsignal=INT environment=PATH=\"$VENV_DIR/bin:%(ENV_PATH)s\",HOME=\"/root\",NEXUS_WORKERS=\"$WORKERS\" stderr_logfile=$LOG_DIR/nexus_error.log stdout_logfile=$LOG_DIR/nexus_access.log stderr_logfile_maxbytes=50MB stdout_logfile_maxbytes=50MB stderr_logfile_backups=5 stdout_logfile_backups=5" # Write config (idempotent) mkdir -p "$SUPERVISOR_CONF_DIR" write_config "$CONF_FILE" "$SUPERVISOR_CONF" # Ensure Supervisor is running if ! supervisorctl status >/dev/null 2>&1; then if [ "$BT_MODE" = true ]; then if [ -f /etc/init.d/supervisor ]; then /etc/init.d/supervisor start >/dev/null 2>&1 || true fi else systemctl start supervisor >/dev/null 2>&1 || true fi sleep 2 fi # Reload Supervisor config supervisorctl reread >/dev/null 2>&1 || true supervisorctl update >/dev/null 2>&1 || true info " Supervisor config written to $CONF_FILE (workers=$WORKERS)" fi # ================================================================ # Phase 6: Nginx Configuration # ================================================================ if [ "$SKIP_NGINX" = true ]; then warn "Phase 6/7: Skipping Nginx (per --skip-nginx)" else info "Phase 6/7: Configuring Nginx..." if [ "$BT_MODE" = true ]; then NGINX_CONF_FILE="/www/server/panel/vhost/nginx/$DOMAIN.conf" NGINX_RELOAD_CMD="/www/server/nginx/sbin/nginx -s reload" NGINX_TEST_CMD="/www/server/nginx/sbin/nginx -t" NGINX_ACCESS_LOG="/www/wwwlogs/$DOMAIN.log" NGINX_ERROR_LOG="/www/wwwlogs/$DOMAIN.error.log" else NGINX_CONF_FILE="/etc/nginx/sites-available/nexus" NGINX_RELOAD_CMD="systemctl reload nginx" NGINX_TEST_CMD="nginx -t" NGINX_ACCESS_LOG="/var/log/nginx/nexus_access.log" NGINX_ERROR_LOG="/var/log/nginx/nexus_error.log" fi NGINX_CONF="# Nexus 6.0 — Nginx Reverse Proxy # Auto-generated by install.sh upstream nexus_api { server 127.0.0.1:$PORT; keepalive 32; } server { listen 80; server_name $DOMAIN; # Reverse proxy to Nexus (uvicorn) location / { proxy_pass http://nexus_api; 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_read_timeout 300s; proxy_send_timeout 300s; } # WebSocket support (terminal, alerts) location /ws/ { proxy_pass http://nexus_api; 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_read_timeout 3600s; } # Static assets caching location /app/vendor/ { proxy_pass http://nexus_api; expires 7d; add_header Cache-Control \"public, immutable\"; } # Block sensitive files location ~ ^/(\\.env|\\.git|\\.svn) { return 404; } # Block config.json access location ~ /data/config\\.json$ { return 404; } # Upload limit client_max_body_size 500m; # Well-known for SSL cert validation location ~ \\.well-known { allow all; } access_log $NGINX_ACCESS_LOG; error_log $NGINX_ERROR_LOG; }" # Write Nginx config if [ "$BT_MODE" = true ]; then write_config "$NGINX_CONF_FILE" "$NGINX_CONF" else mkdir -p /etc/nginx/sites-available write_config "$NGINX_CONF_FILE" "$NGINX_CONF" if [ ! -L /etc/nginx/sites-enabled/nexus ]; then ln -sf "$NGINX_CONF_FILE" /etc/nginx/sites-enabled/nexus fi fi # Test and reload Nginx if $NGINX_TEST_CMD >/dev/null 2>&1; then $NGINX_RELOAD_CMD >/dev/null 2>&1 || warn " Nginx reload failed" info " Nginx config written to $NGINX_CONF_FILE" else warn " Nginx config test failed. Please check $NGINX_CONF_FILE manually" fi fi # ================================================================ # Phase 7: Start Application + Verify # ================================================================ info "Phase 7/7: Starting application..." # Start via Supervisor if [ "$SKIP_SUPERVISOR" = false ]; then supervisorctl start nexus >/dev/null 2>&1 || supervisorctl restart nexus >/dev/null 2>&1 || true fi # Wait for health check HEALTH_OK=false for i in $(seq 1 15); do if curl -sf "http://127.0.0.1:$PORT/health" >/dev/null 2>&1; then HEALTH_OK=true break fi sleep 2 done # ── Print Results ── echo "" echo "==========================================" if [ "$HEALTH_OK" = true ]; then echo -e " ${GREEN}Nexus installed successfully!${NC}" else echo -e " ${YELLOW}Nexus process started but health check pending.${NC}" echo -e " ${YELLOW}It may need a few more seconds to start up.${NC}" fi echo "==========================================" echo "" echo " Deploy: $DEPLOY_DIR" echo " Domain: $DOMAIN" echo " Port: $PORT" echo " Workers: ${WORKERS:-N/A}" echo " BT Panel: $BT_MODE" echo "" if [ "$HEALTH_OK" = true ]; then echo -e " ${GREEN}Next step:${NC} Open your browser and complete the web install wizard:" echo "" echo " http://$DOMAIN/app/install.html" echo "" echo " The wizard will guide you through:" echo " Step 1: Welcome & prerequisites" echo " Step 2: Environment check" echo " Step 3: MySQL + Redis configuration" echo " Step 4: Admin account + brand" echo " Step 5: Complete" else echo " Troubleshooting:" echo " supervisorctl status nexus" echo " tail -f $LOG_DIR/nexus_error.log" echo " curl http://127.0.0.1:$PORT/health" fi echo "" echo " Post-install:" echo " 1. Configure SSL (HTTPS)" if [ "$BT_MODE" = true ]; then echo " BT Panel -> Website -> $DOMAIN -> SSL" else echo " certbot --nginx -d $DOMAIN" fi echo " 2. Set up health monitor crontab:" echo " * * * * * $DEPLOY_DIR/deploy/health_monitor.sh" echo " 3. Set up database backup crontab:" echo " 0 3 * * * $DEPLOY_DIR/deploy/db_backup.sh" echo "" echo " Management:" echo " supervisorctl status nexus" echo " supervisorctl restart nexus" echo " supervisorctl stop nexus" echo ""