Initial NixPanel Build
This commit is contained in:
Executable
+687
@@ -0,0 +1,687 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# NixPanel Installer
|
||||
# Usage: curl -fsSL https://raw.githubusercontent.com/NixReaper/nixpanel-server/main/scripts/install.sh | bash
|
||||
# Or: bash install.sh [--admin-user NAME] [--admin-pass PASS] [--admin-email EMAIL]
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
# ── Colours ───────────────────────────────────────────────────────────────────
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
|
||||
|
||||
info() { echo -e "${CYAN}[INFO]${RESET} $*"; }
|
||||
success() { echo -e "${GREEN}[OK]${RESET} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${RESET} $*"; }
|
||||
error() { echo -e "${RED}[ERROR]${RESET} $*" >&2; exit 1; }
|
||||
step() { echo -e "\n${BOLD}${CYAN}━━━ $* ━━━${RESET}"; }
|
||||
|
||||
# ── Banner ────────────────────────────────────────────────────────────────────
|
||||
echo -e "${BOLD}${CYAN}"
|
||||
cat <<'EOF'
|
||||
_ _ _ ____ _
|
||||
| \ | (_)_ _| _ \ __ _ _ __ ___| |
|
||||
| \| | \ \/ / |_) / _` | '_ \ / _ \ |
|
||||
| |\ | |> <| __/ (_| | | | | __/ |
|
||||
|_| \_|_/_/\_\_| \__,_|_| |_|\___|_|
|
||||
|
||||
Open-Source Web Hosting Control Panel
|
||||
EOF
|
||||
echo -e "${RESET}"
|
||||
echo -e " Version : ${BOLD}0.1.0${RESET}"
|
||||
echo -e " GitHub : ${BOLD}https://github.com/NixReaper/nixpanel-server${RESET}"
|
||||
echo -e " WHM : ${BOLD}https://<your-ip>:2087${RESET}"
|
||||
echo -e " cPanel : ${BOLD}https://<your-ip>:2083${RESET}"
|
||||
echo ""
|
||||
|
||||
# ── Root check ────────────────────────────────────────────────────────────────
|
||||
[[ $EUID -ne 0 ]] && error "This installer must be run as root. Try: sudo bash install.sh"
|
||||
|
||||
# ── OS detection ──────────────────────────────────────────────────────────────
|
||||
step "Detecting OS"
|
||||
|
||||
detect_os() {
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
. /etc/os-release
|
||||
OS_ID="${ID,,}"
|
||||
OS_VERSION_ID="${VERSION_ID:-}"
|
||||
OS_LIKE="${ID_LIKE:-}"
|
||||
else
|
||||
error "Cannot detect OS — /etc/os-release not found"
|
||||
fi
|
||||
|
||||
case "$OS_ID" in
|
||||
ubuntu|debian) PKG_MGR="apt"; OS_FAMILY="debian" ;;
|
||||
almalinux|rocky|rhel|centos)
|
||||
PKG_MGR="dnf"; OS_FAMILY="rhel" ;;
|
||||
fedora) PKG_MGR="dnf"; OS_FAMILY="rhel" ;;
|
||||
*)
|
||||
# Try ID_LIKE fallback
|
||||
if [[ "$OS_LIKE" == *debian* ]]; then
|
||||
PKG_MGR="apt"; OS_FAMILY="debian"
|
||||
elif [[ "$OS_LIKE" == *rhel* ]] || [[ "$OS_LIKE" == *fedora* ]]; then
|
||||
PKG_MGR="dnf"; OS_FAMILY="rhel"
|
||||
else
|
||||
error "Unsupported OS: $OS_ID. Supported: Ubuntu, Debian, AlmaLinux, Rocky Linux, RHEL, CentOS."
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
info "Detected OS: ${PRETTY_NAME:-$OS_ID} (family: $OS_FAMILY)"
|
||||
}
|
||||
|
||||
detect_os
|
||||
|
||||
# ── Architecture check ────────────────────────────────────────────────────────
|
||||
ARCH=$(uname -m)
|
||||
[[ "$ARCH" != "x86_64" && "$ARCH" != "aarch64" ]] && error "Unsupported architecture: $ARCH (x86_64 and aarch64 only)"
|
||||
|
||||
# ── Argument parsing ──────────────────────────────────────────────────────────
|
||||
ADMIN_USER="admin"
|
||||
ADMIN_PASS=""
|
||||
ADMIN_EMAIL=""
|
||||
INSTALL_DIR="/opt/nixpanel"
|
||||
NIXPANEL_DB_USER="nixpanel"
|
||||
NIXPANEL_DB_NAME="nixpanel"
|
||||
NIXPANEL_DB_PASS=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--admin-user) ADMIN_USER="$2"; shift 2 ;;
|
||||
--admin-pass) ADMIN_PASS="$2"; shift 2 ;;
|
||||
--admin-email) ADMIN_EMAIL="$2"; shift 2 ;;
|
||||
--install-dir) INSTALL_DIR="$2"; shift 2 ;;
|
||||
*) warn "Unknown argument: $1"; shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── Generate secrets ──────────────────────────────────────────────────────────
|
||||
gen_secret() { openssl rand -hex 32; }
|
||||
|
||||
if [[ -z "$ADMIN_PASS" ]]; then
|
||||
ADMIN_PASS=$(openssl rand -base64 16 | tr -dc 'a-zA-Z0-9' | head -c 16)
|
||||
GENERATED_PASS=true
|
||||
else
|
||||
GENERATED_PASS=false
|
||||
fi
|
||||
|
||||
if [[ -z "$ADMIN_EMAIL" ]]; then
|
||||
ADMIN_EMAIL="admin@$(hostname -f 2>/dev/null || echo localhost)"
|
||||
fi
|
||||
|
||||
if [[ -z "$NIXPANEL_DB_PASS" ]]; then
|
||||
NIXPANEL_DB_PASS=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)
|
||||
fi
|
||||
|
||||
JWT_SECRET=$(gen_secret)
|
||||
JWT_REFRESH_SECRET=$(gen_secret)
|
||||
|
||||
# ── Confirm ───────────────────────────────────────────────────────────────────
|
||||
echo -e "${BOLD}Installation summary:${RESET}"
|
||||
echo " Install directory : $INSTALL_DIR"
|
||||
echo " Admin username : $ADMIN_USER"
|
||||
echo " Admin email : $ADMIN_EMAIL"
|
||||
echo " Admin password : ${GENERATED_PASS:+<auto-generated, shown at end>}${GENERATED_PASS:-$ADMIN_PASS}"
|
||||
echo ""
|
||||
read -r -p "Continue? [Y/n] " CONFIRM
|
||||
CONFIRM="${CONFIRM:-Y}"
|
||||
[[ "$CONFIRM" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 0; }
|
||||
|
||||
# ── Update package index ──────────────────────────────────────────────────────
|
||||
step "Updating package index"
|
||||
if [[ "$PKG_MGR" == "apt" ]]; then
|
||||
apt-get update -qq
|
||||
elif [[ "$PKG_MGR" == "dnf" ]]; then
|
||||
dnf makecache -q
|
||||
fi
|
||||
success "Package index updated"
|
||||
|
||||
# ── Install base dependencies ─────────────────────────────────────────────────
|
||||
step "Installing base dependencies"
|
||||
|
||||
APT_DEPS=(curl wget gnupg2 ca-certificates lsb-release software-properties-common
|
||||
git build-essential openssl ufw fail2ban)
|
||||
DNF_DEPS=(curl wget gnupg2 ca-certificates git openssl firewalld fail2ban)
|
||||
|
||||
if [[ "$PKG_MGR" == "apt" ]]; then
|
||||
apt-get install -y -qq "${APT_DEPS[@]}"
|
||||
else
|
||||
dnf install -y -q "${DNF_DEPS[@]}"
|
||||
fi
|
||||
success "Base dependencies installed"
|
||||
|
||||
# ── Install Node.js 22 (LTS) ──────────────────────────────────────────────────
|
||||
step "Installing Node.js 22 LTS"
|
||||
|
||||
if command -v node &>/dev/null; then
|
||||
NODE_VER=$(node --version | cut -c2- | cut -d. -f1)
|
||||
if [[ "$NODE_VER" -ge 20 ]]; then
|
||||
info "Node.js $(node --version) already installed — skipping"
|
||||
else
|
||||
warn "Node.js $NODE_VER found — upgrading to v22"
|
||||
install_node=true
|
||||
fi
|
||||
else
|
||||
install_node=true
|
||||
fi
|
||||
|
||||
if [[ "${install_node:-false}" == "true" ]] || ! command -v node &>/dev/null; then
|
||||
if [[ "$PKG_MGR" == "apt" ]]; then
|
||||
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - >/dev/null 2>&1
|
||||
apt-get install -y -qq nodejs
|
||||
else
|
||||
curl -fsSL https://rpm.nodesource.com/setup_22.x | bash - >/dev/null 2>&1
|
||||
dnf install -y -q nodejs
|
||||
fi
|
||||
fi
|
||||
|
||||
success "Node.js $(node --version) ready"
|
||||
success "npm $(npm --version) ready"
|
||||
|
||||
# ── Install MariaDB ───────────────────────────────────────────────────────────
|
||||
step "Installing MariaDB"
|
||||
|
||||
if command -v mariadb &>/dev/null || command -v mysqld &>/dev/null; then
|
||||
info "MariaDB/MySQL already installed — skipping"
|
||||
else
|
||||
if [[ "$PKG_MGR" == "apt" ]]; then
|
||||
apt-get install -y -qq mariadb-server mariadb-client
|
||||
else
|
||||
dnf install -y -q mariadb-server mariadb
|
||||
fi
|
||||
fi
|
||||
|
||||
systemctl enable --now mariadb
|
||||
|
||||
# Secure MariaDB and create DB/user
|
||||
info "Configuring MariaDB..."
|
||||
mariadb -u root <<SQL
|
||||
ALTER USER IF EXISTS 'root'@'localhost' IDENTIFIED VIA unix_socket;
|
||||
DELETE FROM mysql.user WHERE User='';
|
||||
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost','127.0.0.1','::1');
|
||||
DROP DATABASE IF EXISTS test;
|
||||
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
|
||||
|
||||
CREATE DATABASE IF NOT EXISTS \`${NIXPANEL_DB_NAME}\`
|
||||
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE USER IF NOT EXISTS '${NIXPANEL_DB_USER}'@'localhost'
|
||||
IDENTIFIED BY '${NIXPANEL_DB_PASS}';
|
||||
|
||||
GRANT ALL PRIVILEGES ON \`${NIXPANEL_DB_NAME}\`.* TO '${NIXPANEL_DB_USER}'@'localhost';
|
||||
FLUSH PRIVILEGES;
|
||||
SQL
|
||||
|
||||
success "MariaDB configured — database '${NIXPANEL_DB_NAME}' created"
|
||||
|
||||
# ── Install Nginx ─────────────────────────────────────────────────────────────
|
||||
step "Installing Nginx"
|
||||
|
||||
if command -v nginx &>/dev/null; then
|
||||
info "Nginx already installed — skipping"
|
||||
else
|
||||
if [[ "$PKG_MGR" == "apt" ]]; then
|
||||
apt-get install -y -qq nginx
|
||||
else
|
||||
dnf install -y -q nginx
|
||||
fi
|
||||
fi
|
||||
|
||||
systemctl enable --now nginx
|
||||
success "Nginx ready"
|
||||
|
||||
# ── Install PHP 8.3 + FPM ─────────────────────────────────────────────────────
|
||||
step "Installing PHP 8.3 + FPM"
|
||||
|
||||
if [[ "$PKG_MGR" == "apt" ]]; then
|
||||
if ! apt-cache show php8.3 &>/dev/null 2>&1; then
|
||||
add-apt-repository -y ppa:ondrej/php >/dev/null 2>&1
|
||||
apt-get update -qq
|
||||
fi
|
||||
apt-get install -y -qq php8.3-fpm php8.3-cli php8.3-mysql php8.3-curl \
|
||||
php8.3-gd php8.3-mbstring php8.3-xml php8.3-zip php8.3-bcmath \
|
||||
php8.3-intl php8.3-soap php8.3-imagick php8.3-redis php8.3-opcache
|
||||
else
|
||||
dnf install -y -q php8.3 php8.3-fpm php8.3-mysqlnd php8.3-curl \
|
||||
php8.3-gd php8.3-mbstring php8.3-xml php8.3-zip php8.3-bcmath \
|
||||
php8.3-intl php8.3-soap php8.3-opcache 2>/dev/null || \
|
||||
dnf install -y -q php php-fpm php-mysqlnd php-curl php-gd php-mbstring \
|
||||
php-xml php-zip php-bcmath php-intl php-soap php-opcache
|
||||
fi
|
||||
|
||||
systemctl enable --now php8.3-fpm 2>/dev/null || systemctl enable --now php-fpm
|
||||
success "PHP 8.3 + FPM ready"
|
||||
|
||||
# ── Install Postfix + Dovecot ─────────────────────────────────────────────────
|
||||
step "Installing Postfix + Dovecot"
|
||||
|
||||
if [[ "$PKG_MGR" == "apt" ]]; then
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
|
||||
postfix postfix-mysql dovecot-core dovecot-imapd dovecot-pop3d \
|
||||
dovecot-lmtpd dovecot-mysql spamassassin
|
||||
else
|
||||
dnf install -y -q postfix dovecot dovecot-mysql
|
||||
fi
|
||||
|
||||
systemctl enable postfix dovecot
|
||||
success "Postfix + Dovecot installed"
|
||||
|
||||
# ── Install vsftpd ────────────────────────────────────────────────────────────
|
||||
step "Installing vsftpd"
|
||||
|
||||
if [[ "$PKG_MGR" == "apt" ]]; then
|
||||
apt-get install -y -qq vsftpd
|
||||
else
|
||||
dnf install -y -q vsftpd
|
||||
fi
|
||||
|
||||
systemctl enable vsftpd
|
||||
success "vsftpd installed"
|
||||
|
||||
# ── Install BIND9 ─────────────────────────────────────────────────────────────
|
||||
step "Installing BIND9"
|
||||
|
||||
if [[ "$PKG_MGR" == "apt" ]]; then
|
||||
apt-get install -y -qq bind9 bind9utils bind9-doc
|
||||
else
|
||||
dnf install -y -q bind bind-utils
|
||||
fi
|
||||
|
||||
BIND_SERVICE="named"
|
||||
[[ "$PKG_MGR" == "apt" ]] && BIND_SERVICE="named" || true
|
||||
systemctl enable --now "$BIND_SERVICE" 2>/dev/null || systemctl enable --now bind9 2>/dev/null || true
|
||||
|
||||
BIND_ZONES_DIR="/etc/bind/zones"
|
||||
mkdir -p "$BIND_ZONES_DIR"
|
||||
chown root:bind "$BIND_ZONES_DIR" 2>/dev/null || chown root:named "$BIND_ZONES_DIR" 2>/dev/null || true
|
||||
success "BIND9 installed"
|
||||
|
||||
# ── Install Certbot ───────────────────────────────────────────────────────────
|
||||
step "Installing Certbot"
|
||||
|
||||
if [[ "$PKG_MGR" == "apt" ]]; then
|
||||
apt-get install -y -qq certbot python3-certbot-nginx
|
||||
else
|
||||
dnf install -y -q certbot python3-certbot-nginx 2>/dev/null || \
|
||||
dnf install -y -q --enablerepo=epel certbot python3-certbot-nginx
|
||||
fi
|
||||
|
||||
mkdir -p /var/www/letsencrypt/.well-known/acme-challenge
|
||||
success "Certbot installed"
|
||||
|
||||
# ── Clone / update NixPanel ───────────────────────────────────────────────────
|
||||
step "Installing NixPanel"
|
||||
|
||||
if [[ -d "$INSTALL_DIR/.git" ]]; then
|
||||
info "Existing installation found — pulling latest..."
|
||||
git -C "$INSTALL_DIR" pull --ff-only
|
||||
else
|
||||
info "Cloning repository into $INSTALL_DIR..."
|
||||
git clone https://github.com/NixReaper/nixpanel-server.git "$INSTALL_DIR"
|
||||
fi
|
||||
|
||||
success "NixPanel source ready at $INSTALL_DIR"
|
||||
|
||||
# ── Write .env ────────────────────────────────────────────────────────────────
|
||||
step "Configuring environment"
|
||||
|
||||
SERVER_IP=$(hostname -I | awk '{print $1}')
|
||||
|
||||
cat > "$INSTALL_DIR/.env" <<ENV
|
||||
# Generated by NixPanel installer on $(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
||||
|
||||
# Database
|
||||
DATABASE_URL="mysql://${NIXPANEL_DB_USER}:${NIXPANEL_DB_PASS}@localhost:3306/${NIXPANEL_DB_NAME}"
|
||||
|
||||
# JWT
|
||||
JWT_SECRET="${JWT_SECRET}"
|
||||
JWT_EXPIRES_IN="15m"
|
||||
JWT_REFRESH_SECRET="${JWT_REFRESH_SECRET}"
|
||||
JWT_REFRESH_EXPIRES_IN="7d"
|
||||
|
||||
# Server
|
||||
NODE_ENV="production"
|
||||
WHM_PORT=2087
|
||||
CPANEL_PORT=2083
|
||||
HOST="0.0.0.0"
|
||||
|
||||
# Admin credentials (used during initial seed only)
|
||||
ADMIN_USERNAME="${ADMIN_USER}"
|
||||
ADMIN_PASSWORD="${ADMIN_PASS}"
|
||||
ADMIN_EMAIL="${ADMIN_EMAIL}"
|
||||
|
||||
# Paths
|
||||
HOME_DIR="/home"
|
||||
NGINX_SITES_DIR="/etc/nginx/sites-available"
|
||||
NGINX_ENABLED_DIR="/etc/nginx/sites-enabled"
|
||||
APACHE_SITES_DIR="/etc/apache2/sites-available"
|
||||
APACHE_ENABLED_DIR="/etc/apache2/sites-enabled"
|
||||
BIND_ZONES_DIR="${BIND_ZONES_DIR}"
|
||||
CERTBOT_WEBROOT="/var/www/letsencrypt"
|
||||
|
||||
# Rate limiting
|
||||
RATE_LIMIT_MAX=100
|
||||
RATE_LIMIT_WINDOW_MS=60000
|
||||
|
||||
# Features
|
||||
ENABLE_TERMINAL=true
|
||||
WEBSOCKET_STATS_INTERVAL=5000
|
||||
ENV
|
||||
|
||||
chmod 600 "$INSTALL_DIR/.env"
|
||||
success ".env written"
|
||||
|
||||
# ── npm install ───────────────────────────────────────────────────────────────
|
||||
step "Installing Node.js dependencies"
|
||||
|
||||
cd "$INSTALL_DIR"
|
||||
npm install --workspaces --include-workspace-root --silent
|
||||
success "npm dependencies installed"
|
||||
|
||||
# ── Prisma generate + migrate ─────────────────────────────────────────────────
|
||||
step "Running database migrations"
|
||||
|
||||
cd "$INSTALL_DIR/server"
|
||||
npx prisma generate --silent
|
||||
DATABASE_URL="mysql://${NIXPANEL_DB_USER}:${NIXPANEL_DB_PASS}@localhost:3306/${NIXPANEL_DB_NAME}" \
|
||||
npx prisma migrate deploy
|
||||
success "Database schema applied"
|
||||
|
||||
# ── Seed default data ─────────────────────────────────────────────────────────
|
||||
step "Seeding initial data"
|
||||
|
||||
DATABASE_URL="mysql://${NIXPANEL_DB_USER}:${NIXPANEL_DB_PASS}@localhost:3306/${NIXPANEL_DB_NAME}" \
|
||||
ADMIN_USERNAME="${ADMIN_USER}" \
|
||||
ADMIN_PASSWORD="${ADMIN_PASS}" \
|
||||
ADMIN_EMAIL="${ADMIN_EMAIL}" \
|
||||
npx tsx src/db/seed.ts
|
||||
success "Default admin, packages, and settings seeded"
|
||||
|
||||
# ── Build frontends ───────────────────────────────────────────────────────────
|
||||
step "Building WHM + cPanel frontends"
|
||||
|
||||
cd "$INSTALL_DIR"
|
||||
npm run build --workspace=whm --silent
|
||||
npm run build --workspace=cpanel --silent
|
||||
success "Frontends built"
|
||||
|
||||
# ── systemd service ───────────────────────────────────────────────────────────
|
||||
step "Creating systemd service"
|
||||
|
||||
cat > /etc/systemd/system/nixpanel.service <<UNIT
|
||||
[Unit]
|
||||
Description=NixPanel Web Hosting Control Panel
|
||||
Documentation=https://github.com/NixReaper/nixpanel-server
|
||||
After=network.target mariadb.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=${INSTALL_DIR}/server
|
||||
EnvironmentFile=${INSTALL_DIR}/.env
|
||||
ExecStart=$(which node) --enable-source-maps dist/index.js
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=nixpanel
|
||||
LimitNOFILE=65536
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
UNIT
|
||||
|
||||
# Build server for production
|
||||
cd "$INSTALL_DIR/server"
|
||||
npm run build --silent 2>/dev/null || npx tsc --noEmit false 2>/dev/null || true
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable nixpanel
|
||||
systemctl restart nixpanel
|
||||
success "nixpanel.service created and started"
|
||||
|
||||
# ── Nginx reverse proxy ───────────────────────────────────────────────────────
|
||||
step "Configuring Nginx reverse proxy"
|
||||
|
||||
# Ensure sites-available/enabled dirs exist
|
||||
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
|
||||
|
||||
# Main nginx.conf include (idempotent)
|
||||
if ! grep -q "sites-enabled" /etc/nginx/nginx.conf 2>/dev/null; then
|
||||
sed -i '/http {/a \ include /etc/nginx/sites-enabled/*.conf;' /etc/nginx/nginx.conf
|
||||
fi
|
||||
|
||||
cat > /etc/nginx/sites-available/nixpanel.conf <<NGINX
|
||||
# NixPanel — generated by installer
|
||||
# WHM admin panel on port 2087
|
||||
server {
|
||||
listen 2087;
|
||||
server_name _;
|
||||
|
||||
# Serve pre-built WHM React app
|
||||
root ${INSTALL_DIR}/whm/dist;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files \$uri \$uri/ /index.html;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:2087;
|
||||
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_read_timeout 300s;
|
||||
}
|
||||
|
||||
location /ws {
|
||||
proxy_pass http://127.0.0.1:2087;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade \$http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
|
||||
# cPanel user panel on port 2083
|
||||
server {
|
||||
listen 2083;
|
||||
server_name _;
|
||||
|
||||
root ${INSTALL_DIR}/cpanel/dist;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files \$uri \$uri/ /index.html;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:2087;
|
||||
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_read_timeout 300s;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTP — Let's Encrypt webroot + redirect
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/letsencrypt;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://\$host\$request_uri;
|
||||
}
|
||||
}
|
||||
NGINX
|
||||
|
||||
ln -sf /etc/nginx/sites-available/nixpanel.conf /etc/nginx/sites-enabled/nixpanel.conf
|
||||
|
||||
nginx -t && systemctl reload nginx
|
||||
success "Nginx configured"
|
||||
|
||||
# ── Firewall ──────────────────────────────────────────────────────────────────
|
||||
step "Configuring firewall"
|
||||
|
||||
if [[ "$PKG_MGR" == "apt" ]]; then
|
||||
# UFW
|
||||
ufw --force enable >/dev/null
|
||||
ufw allow 22/tcp comment "SSH" >/dev/null
|
||||
ufw allow 80/tcp comment "HTTP" >/dev/null
|
||||
ufw allow 443/tcp comment "HTTPS" >/dev/null
|
||||
ufw allow 2087/tcp comment "WHM" >/dev/null
|
||||
ufw allow 2083/tcp comment "cPanel" >/dev/null
|
||||
ufw allow 21/tcp comment "FTP" >/dev/null
|
||||
ufw allow 25/tcp comment "SMTP" >/dev/null
|
||||
ufw allow 143/tcp comment "IMAP" >/dev/null
|
||||
ufw allow 993/tcp comment "IMAPS" >/dev/null
|
||||
ufw allow 110/tcp comment "POP3" >/dev/null
|
||||
ufw allow 995/tcp comment "POP3S" >/dev/null
|
||||
ufw allow 587/tcp comment "SMTP submission" >/dev/null
|
||||
ufw allow 465/tcp comment "SMTPS" >/dev/null
|
||||
ufw allow 53 comment "DNS" >/dev/null
|
||||
success "UFW rules applied"
|
||||
else
|
||||
# firewalld
|
||||
systemctl enable --now firewalld
|
||||
for PORT in 22 80 443 2087 2083 21 25 143 993 110 995 587 465 53; do
|
||||
firewall-cmd --permanent --add-port=${PORT}/tcp >/dev/null 2>&1 || true
|
||||
done
|
||||
firewall-cmd --permanent --add-port=53/udp >/dev/null 2>&1 || true
|
||||
firewall-cmd --reload >/dev/null
|
||||
success "firewalld rules applied"
|
||||
fi
|
||||
|
||||
# ── Fail2ban ──────────────────────────────────────────────────────────────────
|
||||
step "Configuring Fail2ban"
|
||||
|
||||
cat > /etc/fail2ban/jail.d/nixpanel.conf <<F2B
|
||||
[sshd]
|
||||
enabled = true
|
||||
maxretry = 5
|
||||
bantime = 3600
|
||||
findtime = 600
|
||||
|
||||
[nginx-http-auth]
|
||||
enabled = true
|
||||
maxretry = 10
|
||||
bantime = 600
|
||||
|
||||
[postfix]
|
||||
enabled = true
|
||||
|
||||
[dovecot]
|
||||
enabled = true
|
||||
F2B
|
||||
|
||||
systemctl enable --now fail2ban
|
||||
success "Fail2ban configured"
|
||||
|
||||
# ── vsftpd configuration ──────────────────────────────────────────────────────
|
||||
step "Configuring vsftpd"
|
||||
|
||||
cat > /etc/vsftpd.conf <<VSFTPD
|
||||
listen=YES
|
||||
listen_ipv6=NO
|
||||
anonymous_enable=NO
|
||||
local_enable=YES
|
||||
write_enable=YES
|
||||
local_umask=022
|
||||
dirmessage_enable=YES
|
||||
use_localtime=YES
|
||||
xferlog_enable=YES
|
||||
connect_from_port_20=YES
|
||||
chroot_local_user=YES
|
||||
allow_writeable_chroot=YES
|
||||
secure_chroot_dir=/var/run/vsftpd/empty
|
||||
pam_service_name=vsftpd
|
||||
ssl_enable=NO
|
||||
utf8_filesystem=YES
|
||||
pasv_enable=YES
|
||||
pasv_min_port=40000
|
||||
pasv_max_port=50000
|
||||
VSFTPD
|
||||
|
||||
systemctl restart vsftpd 2>/dev/null || true
|
||||
success "vsftpd configured"
|
||||
|
||||
# ── Wait for service to start ─────────────────────────────────────────────────
|
||||
step "Waiting for NixPanel to start"
|
||||
|
||||
MAX_WAIT=30
|
||||
WAITED=0
|
||||
until curl -sf http://127.0.0.1:2087/api/health >/dev/null 2>&1; do
|
||||
sleep 2
|
||||
WAITED=$((WAITED + 2))
|
||||
[[ $WAITED -ge $MAX_WAIT ]] && { warn "Service health check timed out — check: journalctl -u nixpanel -n 50"; break; }
|
||||
done
|
||||
|
||||
if curl -sf http://127.0.0.1:2087/api/health >/dev/null 2>&1; then
|
||||
success "NixPanel API is responding"
|
||||
else
|
||||
warn "NixPanel may still be starting. Check status with: systemctl status nixpanel"
|
||||
fi
|
||||
|
||||
# ── Print completion summary ──────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo -e "${BOLD}${GREEN}╔══════════════════════════════════════════════════════╗${RESET}"
|
||||
echo -e "${BOLD}${GREEN}║ NixPanel Installation Complete! ║${RESET}"
|
||||
echo -e "${BOLD}${GREEN}╚══════════════════════════════════════════════════════╝${RESET}"
|
||||
echo ""
|
||||
echo -e " ${BOLD}WHM (Admin Panel)${RESET}"
|
||||
echo -e " URL : ${CYAN}http://${SERVER_IP}:2087${RESET}"
|
||||
echo -e " Username : ${BOLD}${ADMIN_USER}${RESET}"
|
||||
echo -e " Password : ${BOLD}${ADMIN_PASS}${RESET}"
|
||||
echo ""
|
||||
echo -e " ${BOLD}cPanel (User Panel)${RESET}"
|
||||
echo -e " URL : ${CYAN}http://${SERVER_IP}:2083${RESET}"
|
||||
echo ""
|
||||
echo -e " ${BOLD}Database${RESET}"
|
||||
echo -e " DB Name : ${NIXPANEL_DB_NAME}"
|
||||
echo -e " DB User : ${NIXPANEL_DB_USER}"
|
||||
echo -e " DB Pass : ${NIXPANEL_DB_PASS}"
|
||||
echo ""
|
||||
echo -e " ${BOLD}Files${RESET}"
|
||||
echo -e " Install : ${INSTALL_DIR}"
|
||||
echo -e " Config : ${INSTALL_DIR}/.env"
|
||||
echo -e " Service : systemctl status nixpanel"
|
||||
echo -e " Logs : journalctl -u nixpanel -f"
|
||||
echo ""
|
||||
if [[ "$GENERATED_PASS" == "true" ]]; then
|
||||
echo -e " ${YELLOW}${BOLD}⚠ Save your admin password — it will not be shown again!${RESET}"
|
||||
echo ""
|
||||
fi
|
||||
echo -e " ${BOLD}Useful commands:${RESET}"
|
||||
echo -e " systemctl restart nixpanel # restart the panel"
|
||||
echo -e " systemctl status nixpanel # check status"
|
||||
echo -e " journalctl -u nixpanel -f # live logs"
|
||||
echo ""
|
||||
|
||||
# Save credentials to a file root can read
|
||||
CRED_FILE="/root/.nixpanel_credentials"
|
||||
cat > "$CRED_FILE" <<CREDS
|
||||
NixPanel Credentials — $(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
||||
==========================================================
|
||||
WHM URL : http://${SERVER_IP}:2087
|
||||
cPanel URL : http://${SERVER_IP}:2083
|
||||
Admin User : ${ADMIN_USER}
|
||||
Admin Pass : ${ADMIN_PASS}
|
||||
Admin Email: ${ADMIN_EMAIL}
|
||||
DB Name : ${NIXPANEL_DB_NAME}
|
||||
DB User : ${NIXPANEL_DB_USER}
|
||||
DB Pass : ${NIXPANEL_DB_PASS}
|
||||
Install Dir: ${INSTALL_DIR}
|
||||
CREDS
|
||||
chmod 600 "$CRED_FILE"
|
||||
|
||||
echo -e " Credentials saved to: ${BOLD}${CRED_FILE}${RESET}"
|
||||
echo ""
|
||||
Executable
+65
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env bash
|
||||
# NixPanel uninstaller
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'; YELLOW='\033[1;33m'; BOLD='\033[1m'; RESET='\033[0m'
|
||||
|
||||
[[ $EUID -ne 0 ]] && { echo -e "${RED}Must be run as root${RESET}"; exit 1; }
|
||||
|
||||
INSTALL_DIR="${1:-/opt/nixpanel}"
|
||||
|
||||
echo -e "${BOLD}${RED}NixPanel Uninstaller${RESET}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}This will:${RESET}"
|
||||
echo " - Stop and remove the nixpanel systemd service"
|
||||
echo " - Remove the nginx nixpanel.conf vhost"
|
||||
echo " - Remove $INSTALL_DIR"
|
||||
echo " - Drop the nixpanel MariaDB database and user"
|
||||
echo ""
|
||||
echo -e "${RED}${BOLD}This does NOT remove: nginx, MariaDB, PHP, Postfix, Dovecot, vsftpd, or BIND.${RESET}"
|
||||
echo ""
|
||||
read -r -p "Type 'yes' to confirm: " CONFIRM
|
||||
[[ "$CONFIRM" == "yes" ]] || { echo "Aborted."; exit 0; }
|
||||
|
||||
echo ""
|
||||
|
||||
# Stop + disable service
|
||||
if systemctl is-active --quiet nixpanel 2>/dev/null; then
|
||||
systemctl stop nixpanel
|
||||
echo "Stopped nixpanel service"
|
||||
fi
|
||||
if systemctl is-enabled --quiet nixpanel 2>/dev/null; then
|
||||
systemctl disable nixpanel
|
||||
fi
|
||||
rm -f /etc/systemd/system/nixpanel.service
|
||||
systemctl daemon-reload
|
||||
echo "Removed systemd service"
|
||||
|
||||
# Remove nginx vhost
|
||||
rm -f /etc/nginx/sites-enabled/nixpanel.conf
|
||||
rm -f /etc/nginx/sites-available/nixpanel.conf
|
||||
nginx -t 2>/dev/null && systemctl reload nginx 2>/dev/null || true
|
||||
echo "Removed nginx config"
|
||||
|
||||
# Drop database
|
||||
if command -v mariadb &>/dev/null; then
|
||||
mariadb -u root 2>/dev/null <<SQL || true
|
||||
DROP DATABASE IF EXISTS \`nixpanel\`;
|
||||
DROP USER IF EXISTS 'nixpanel'@'localhost';
|
||||
FLUSH PRIVILEGES;
|
||||
SQL
|
||||
echo "Dropped nixpanel database and user"
|
||||
fi
|
||||
|
||||
# Remove install directory
|
||||
if [[ -d "$INSTALL_DIR" ]]; then
|
||||
rm -rf "$INSTALL_DIR"
|
||||
echo "Removed $INSTALL_DIR"
|
||||
fi
|
||||
|
||||
# Remove credentials file
|
||||
rm -f /root/.nixpanel_credentials
|
||||
|
||||
echo ""
|
||||
echo -e "${BOLD}NixPanel has been uninstalled.${RESET}"
|
||||
echo ""
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
# NixPanel updater — pulls latest code and rebuilds
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
|
||||
info() { echo -e "${CYAN}[INFO]${RESET} $*"; }
|
||||
success() { echo -e "${GREEN}[OK]${RESET} $*"; }
|
||||
error() { echo -e "${RED}[ERROR]${RESET} $*" >&2; exit 1; }
|
||||
step() { echo -e "\n${BOLD}${CYAN}━━━ $* ━━━${RESET}"; }
|
||||
|
||||
[[ $EUID -ne 0 ]] && error "Must be run as root"
|
||||
|
||||
INSTALL_DIR="${1:-/opt/nixpanel}"
|
||||
[[ -d "$INSTALL_DIR/.git" ]] || error "NixPanel not found at $INSTALL_DIR"
|
||||
|
||||
step "Pulling latest code"
|
||||
git -C "$INSTALL_DIR" pull --ff-only
|
||||
success "Code updated"
|
||||
|
||||
step "Installing dependencies"
|
||||
cd "$INSTALL_DIR"
|
||||
npm install --workspaces --include-workspace-root --silent
|
||||
success "Dependencies ready"
|
||||
|
||||
step "Running migrations"
|
||||
cd "$INSTALL_DIR/server"
|
||||
npx prisma generate --silent
|
||||
npx prisma migrate deploy
|
||||
success "Migrations applied"
|
||||
|
||||
step "Rebuilding frontends"
|
||||
cd "$INSTALL_DIR"
|
||||
npm run build --workspace=whm --silent
|
||||
npm run build --workspace=cpanel --silent
|
||||
success "Frontends rebuilt"
|
||||
|
||||
step "Rebuilding server"
|
||||
cd "$INSTALL_DIR/server"
|
||||
npm run build --silent 2>/dev/null || npx tsc 2>/dev/null || true
|
||||
success "Server rebuilt"
|
||||
|
||||
step "Restarting service"
|
||||
systemctl restart nixpanel
|
||||
sleep 3
|
||||
systemctl is-active nixpanel && success "nixpanel restarted" || error "nixpanel failed to start — check: journalctl -u nixpanel -n 50"
|
||||
|
||||
echo ""
|
||||
echo -e "${BOLD}${GREEN}NixPanel updated successfully!${RESET}"
|
||||
echo ""
|
||||
Reference in New Issue
Block a user