Files
nixpanel-server/scripts/install.sh
T

701 lines
26 KiB
Bash
Executable File

#!/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.2.0${RESET}"
echo -e " GitHub : ${BOLD}https://github.com/NixReaper/nixpanel-server${RESET}"
echo -e " NixServer: ${BOLD}https://<your-ip>:2087${RESET}"
echo -e " NixClient: ${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"
if [[ "$GENERATED_PASS" == "true" ]]; then
echo " Admin password : <auto-generated, shown at end>"
else
echo " Admin password : $ADMIN_PASS"
fi
echo ""
read -r -p "Continue? [Y/n] " CONFIRM
CONFIRM="${CONFIRM:-Y}"
[[ "$CONFIRM" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 0; }
# ── Logging ───────────────────────────────────────────────────────────────────
LOG_FILE="/var/log/nixpanel-install.log"
exec > >(tee -a "$LOG_FILE") 2>&1
echo "=================================================="
echo " NixPanel Install Log — $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
echo " OS: ${PRETTY_NAME:-$OS_ID} Arch: $(uname -m)"
echo "=================================================="
# ── 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 OR REPLACE USER '${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"
NIXSERVER_PORT=2087
NIXCLIENT_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
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 NixServer + NixClient frontends"
cd "$INSTALL_DIR"
npm run build --workspace=nixserver --silent
npm run build --workspace=nixclient --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
# NixServer admin panel on port 2087
server {
listen 2087;
server_name _;
# Serve pre-built NixServer React app
root ${INSTALL_DIR}/nixserver/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";
}
}
# NixClient user panel on port 2083
server {
listen 2083;
server_name _;
root ${INSTALL_DIR}/nixclient/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 "NixServer" >/dev/null
ufw allow 2083/tcp comment "NixClient" >/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}NixServer (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}NixClient (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 " Install log: ${LOG_FILE}"
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')
==========================================================
NixServer URL : http://${SERVER_IP}:2087
NixClient 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 ""