Files
Nexus/deploy/install.sh
T
2026-07-08 22:31:31 +08:00

527 lines
16 KiB
Bash

#!/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 ""