Files
lxs/apps/pterodactyl.sh
hykocx 15c42e1f24 feat(lib): add require_disk_space helper and enforce pre-flight disk checks
- add `require_disk_space` function to lib/common.sh with dedup logic for shared filesystems
- gate cloudpanel, coolify, pterodactyl installs behind a 2–3 GB disk check
- gate uptime-kuma, proxmox update, harden, update-server, and server-benchmark behind 300–1024 MB disk checks
- fail early with a clear error before apt installs or config writes can leave the system in a partial state
2026-05-12 22:39:19 -04:00

338 lines
12 KiB
Bash
Executable File

#!/bin/bash
# LXS - Pterodactyl Installation Script
# Description: Install Pterodactyl Panel + Wings
# Author: LXS
# Date: 2025
# Load LXS common library (colors, separator, run_spinner, loggers, helpers)
LXS_RAW_BASE="${LXS_RAW_BASE:-https://git.hyko.cx/hykocx/lxs/raw/branch/main}"
_lib=$(curl -fsSL "${LXS_RAW_BASE}/lib/common.sh") || { echo "Failed to fetch lib/common.sh" >&2; exit 1; }
eval "$_lib"
unset _lib
export LXS_LOG_FILE="/tmp/lxs_pterodactyl.log"
require_root "$0" "$@"
# ═══════════════════════════════════════════════════════════════════════════
# Configuration
# ═══════════════════════════════════════════════════════════════════════════
PANEL_DIR="/var/www/pterodactyl"
WINGS_DIR="/etc/pterodactyl"
DB_NAME="panel"
DB_USER="pterodactyl"
DB_HOST="127.0.0.1"
# ═══════════════════════════════════════════════════════════════════════════
# Helper Functions
# ═══════════════════════════════════════════════════════════════════════════
is_installed() {
[ -d "$PANEL_DIR" ] && systemctl list-unit-files | grep -q "pteroq.service"
}
# ═══════════════════════════════════════════════════════════════════════════
# Installation Functions
# ═══════════════════════════════════════════════════════════════════════════
install_dependencies() {
apt_noninteractive
run_spinner "Updating system..." "apt update -qq"
run_spinner "Installing dependencies..." "apt install -y -qq software-properties-common curl -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'"
run_spinner "Adding PHP repository..." "LC_ALL=C.UTF-8 add-apt-repository -y ppa:ondrej/php && apt update -qq"
run_spinner "Installing PHP and services..." "apt install -y -qq php8.3 php8.3-{cli,gd,mysql,mbstring,bcmath,xml,fpm,curl,zip} mariadb-server nginx tar unzip git redis-server -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'"
}
install_composer() {
run_spinner "Installing Composer..." "curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer --quiet"
}
setup_database() {
DB_PASS=$(generate_password)
echo -e "${PURPLE}[*] Configuring database...${NC}"
systemctl start mariadb && systemctl enable mariadb
mysql -u root <<MYSQL_SCRIPT
CREATE DATABASE IF NOT EXISTS ${DB_NAME};
CREATE USER IF NOT EXISTS '${DB_USER}'@'${DB_HOST}' IDENTIFIED BY '${DB_PASS}';
GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'${DB_HOST}';
FLUSH PRIVILEGES;
MYSQL_SCRIPT
[ $? -eq 0 ] && echo -e "${GREEN}[✓] Database configured${NC}" || { echo -e "${RED}[✗] Database failed${NC}"; return 1; }
}
install_panel() {
mkdir -p $PANEL_DIR && cd $PANEL_DIR
run_spinner "Downloading Panel..." "curl -Lo panel.tar.gz https://github.com/pterodactyl/panel/releases/latest/download/panel.tar.gz"
run_spinner "Extracting..." "tar -xzf panel.tar.gz"
run_spinner "Setting permissions..." "chmod -R 755 storage/* bootstrap/cache/"
}
configure_panel() {
cd $PANEL_DIR
echo -e "${PURPLE}[*] Installing dependencies (this may take a few minutes)...${NC}"
export COMPOSER_ALLOW_SUPERUSER=1
COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --optimize-autoloader --no-interaction > /tmp/lxs_composer.log 2>&1
cp .env.example .env
php artisan key:generate --force
sed -i "s/DB_DATABASE=.*/DB_DATABASE=${DB_NAME}/" .env
sed -i "s/DB_USERNAME=.*/DB_USERNAME=${DB_USER}/" .env
sed -i "s/DB_PASSWORD=.*/DB_PASSWORD=${DB_PASS}/" .env
run_spinner "Running migrations..." "php artisan migrate --seed --force"
echo ""
echo -e "${WHITE}${BOLD}Admin Account Setup${NC}"
read -p "Email: " ADMIN_EMAIL
read -p "Username: " ADMIN_USERNAME
read -p "First Name: " ADMIN_FIRSTNAME
read -p "Last Name: " ADMIN_LASTNAME
read -sp "Password: " ADMIN_PASSWORD
echo ""
php artisan p:user:make --email="$ADMIN_EMAIL" --username="$ADMIN_USERNAME" --name-first="$ADMIN_FIRSTNAME" --name-last="$ADMIN_LASTNAME" --password="$ADMIN_PASSWORD" --admin=1 --no-interaction
chown -R www-data:www-data $PANEL_DIR
}
configure_nginx() {
local domain=$1
cat > /etc/nginx/sites-available/pterodactyl.conf <<'EOF'
server {
listen 80;
server_name DOMAIN_PLACEHOLDER;
root /var/www/pterodactyl/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
EOF
sed -i "s/DOMAIN_PLACEHOLDER/$domain/g" /etc/nginx/sites-available/pterodactyl.conf
ln -sf /etc/nginx/sites-available/pterodactyl.conf /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl restart nginx php8.3-fpm
}
configure_ssl() {
local domain=$1
local email=$2
run_spinner "Installing Certbot..." "apt install -y -qq certbot python3-certbot-nginx -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'"
certbot --nginx -d $domain --non-interactive --agree-tos --email $email --redirect 2>/dev/null
[ $? -eq 0 ] && cd $PANEL_DIR && sed -i "s|APP_URL=.*|APP_URL=https://${domain}|" .env
}
setup_services() {
cat > /etc/systemd/system/pteroq.service <<'EOF'
[Unit]
Description=Pterodactyl Queue Worker
After=redis-server.service
[Service]
User=www-data
Group=www-data
Restart=always
ExecStart=/usr/bin/php /var/www/pterodactyl/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3
[Install]
WantedBy=multi-user.target
EOF
systemctl enable --now redis-server pteroq.service
(crontab -l 2>/dev/null; echo "* * * * * php /var/www/pterodactyl/artisan schedule:run >> /dev/null 2>&1") | crontab -
}
install_docker() {
run_spinner "Installing Docker..." "curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg && \
echo \"deb [arch=\$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \$(lsb_release -cs) stable\" | tee /etc/apt/sources.list.d/docker.list > /dev/null && \
apt update -qq && apt install -y -qq docker-ce docker-ce-cli containerd.io -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'"
systemctl enable --now docker
}
install_wings() {
mkdir -p $WINGS_DIR
run_spinner "Installing Wings..." "curl -L -o /usr/local/bin/wings https://github.com/pterodactyl/wings/releases/latest/download/wings_linux_amd64 && chmod +x /usr/local/bin/wings"
cat > /etc/systemd/system/wings.service <<'EOF'
[Unit]
Description=Pterodactyl Wings
After=docker.service
Requires=docker.service
[Service]
User=root
WorkingDirectory=/etc/pterodactyl
ExecStart=/usr/local/bin/wings
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
}
# ═══════════════════════════════════════════════════════════════════════════
# Main Installation
# ═══════════════════════════════════════════════════════════════════════════
install_pterodactyl() {
local start_time=$(date +%s)
# Configure non-interactive mode globally
export DEBIAN_FRONTEND=noninteractive
export NEEDRESTART_MODE=a
export NEEDRESTART_SUSPEND=1
echo -e "${WHITE}${BOLD}PTERODACTYL INSTALLATION${NC}\n"
if is_installed; then
echo -e "${YELLOW}Pterodactyl is already installed!${NC}"
return 0
fi
require_disk_space 3072 || return 1
echo "[1/10] Installing dependencies..."
install_dependencies && install_composer
echo ""
echo "[2/10] Setting up database..."
setup_database
echo ""
echo "[3/10] Installing Panel..."
install_panel
echo ""
echo "[4/10] Configuring Panel..."
configure_panel
echo ""
echo "[5/10] Domain configuration..."
read -p "Panel domain (e.g., panel.example.com): " PANEL_DOMAIN
read -p "Wings domain (e.g., wings.example.com): " WINGS_DOMAIN
read -p "Email for SSL: " SSL_EMAIL
echo ""
echo "[6/10] Configuring Nginx..."
configure_nginx "$PANEL_DOMAIN"
echo ""
echo "[7/10] Configuring SSL..."
configure_ssl "$PANEL_DOMAIN" "$SSL_EMAIL"
echo ""
echo "[8/10] Setting up services..."
setup_services
echo ""
echo "[9/10] Installing Wings..."
install_docker && install_wings
echo ""
echo "[10/10] Wings configuration..."
echo -e "${YELLOW}Configure Wings from Panel: Admin -> Nodes -> Create Node${NC}"
echo -e "${GRAY}Node settings:${NC}"
echo -e " FQDN: ${CYAN}$WINGS_DOMAIN${NC}"
echo -e " SSL: ${CYAN}Yes${NC}"
echo -e " Port: ${CYAN}8080${NC}"
echo ""
read -p "Press Enter when ready to paste Wings config..."
echo -e "${CYAN}Paste config and press Ctrl+D:${NC}"
cat > /etc/pterodactyl/config.yml
[ -s /etc/pterodactyl/config.yml ] && systemctl enable --now wings
ufw_allow 80/tcp "Pterodactyl HTTP"
ufw_allow 443/tcp "Pterodactyl HTTPS"
ufw_allow 8080/tcp "Pterodactyl Wings daemon"
ufw_allow 2022/tcp "Pterodactyl Wings SFTP"
local duration=$(( $(date +%s) - start_time ))
echo ""
echo -e "${GREEN}${BOLD}Installation Completed!${NC}"
echo -e "${GRAY}Time: $((duration / 60))m $((duration % 60))s${NC}"
echo ""
echo -e "${CYAN}Panel: ${BOLD}https://$PANEL_DOMAIN${NC}"
echo -e "${CYAN}Wings: ${BOLD}$WINGS_DOMAIN:8080${NC}"
echo ""
echo -e "${WHITE}Database Credentials:${NC}"
echo -e " Host: $DB_HOST"
echo -e " Database: $DB_NAME"
echo -e " User: $DB_USER"
echo -e " Password: $DB_PASS"
}
show_status() {
echo -e "${WHITE}${BOLD}PTERODACTYL STATUS${NC}\n"
if ! is_installed; then
echo -e "${RED}Pterodactyl is not installed!${NC}"
return 1
fi
systemctl is-active --quiet pteroq && echo -e "${GREEN}[✓] Panel Queue${NC}" || echo -e "${RED}[✗] Panel Queue${NC}"
systemctl is-active --quiet nginx && echo -e "${GREEN}[✓] Web Server${NC}" || echo -e "${RED}[✗] Web Server${NC}"
systemctl is-active --quiet wings && echo -e "${GREEN}[✓] Wings${NC}" || echo -e "${YELLOW}[!] Wings (needs config)${NC}"
return 0
}
# ═══════════════════════════════════════════════════════════════════════════
# Main Menu
# ═══════════════════════════════════════════════════════════════════════════
show_menu() {
clear
echo -e "${WHITE}${BOLD}PTERODACTYL MANAGEMENT${NC}\n"
echo -e " ${GREEN}[1]${NC} Install Pterodactyl"
echo -e " ${CYAN}[2]${NC} View Status"
echo -e " ${RED}[0]${NC} Back to main menu"
echo ""
echo -n "Choice [0-2]: "
}
main() {
while true; do
show_menu
read -r choice
echo ""
case $choice in
1) install_pterodactyl ;;
2) show_status ;;
0) return 75 ;;
*) echo -e "${RED}Invalid option${NC}" ;;
esac
echo ""
read -p "Press Enter to continue..."
done
}
main