feat: initial project scaffold for lxs multi-tool

- add main entrypoint with interactive menu and CLI dispatcher (lxs.sh)
- add shared helpers library with colors, loggers, and spinner (lib/common.sh)
- add app installers for coolify, pterodactyl, uptime-kuma, cloudpanel, and proxmox (apps/)
- add system tools for monitoring, benchmarking, and hardening (tools/)
- add VERSION file (0.1.0) as single source of truth for releases
- add MIT LICENSE
- expand README with usage, project structure, and release workflow
This commit is contained in:
2026-05-12 17:29:21 -04:00
parent aad89c1778
commit eadae693a5
13 changed files with 3820 additions and 1 deletions
+637
View File
@@ -0,0 +1,637 @@
#!/bin/bash
# LXS - CloudPanel Installation Script
# Description: Install and manage CloudPanel hosting platform
# Author: LXS
# Date: 2025
# Load LXS common library (colors, separator, run_spinner, loggers)
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_cloudpanel.log"
# ═══════════════════════════════════════════════════════════════════════════
# Configuration
# ═══════════════════════════════════════════════════════════════════════════
INSTALL_DIR="/usr/local/cloudpanel"
PANEL_PORT=8443
INSTALLER_URL="https://installer.cloudpanel.io/ce/v2/install.sh"
INSTALLER_SHA256="19cfa702e7936a79e47812ff57d9859175ea902c62a68b2c15ccd1ebaf36caeb"
# ═══════════════════════════════════════════════════════════════════════════
# Helper Functions
# ═══════════════════════════════════════════════════════════════════════════
is_installed() {
[ -d "$INSTALL_DIR" ] || command -v clpctl &> /dev/null || [ -f "/usr/local/bin/clpctl" ]
}
check_requirements() {
echo -e "${WHITE}Checking System Requirements...${NC}"
# Check root access
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}[✗] This script must be run as root${NC}"
return 1
fi
echo -e "${GREEN}[✓] Running as root${NC}"
local cpu_cores=$(nproc)
[ $cpu_cores -lt 1 ] && echo -e "${RED}[✗] Insufficient CPU cores (minimum: 1)${NC}" && return 1
echo -e "${GREEN}[✓] CPU cores: $cpu_cores${NC}"
local total_mem_gb=$(($(grep MemTotal /proc/meminfo | awk '{print $2}') / 1024 / 1024))
if [ $total_mem_gb -lt 1 ]; then
echo -e "${RED}[✗] Insufficient RAM: ${total_mem_gb}GB (minimum: 1GB)${NC}"
return 1
elif [ $total_mem_gb -lt 2 ]; then
echo -e "${YELLOW}[!] Warning: 2GB RAM is recommended (found: ${total_mem_gb}GB)${NC}"
else
echo -e "${GREEN}[✓] Memory: ${total_mem_gb}GB${NC}"
fi
local available_gb=$(($(df / | awk 'NR==2 {print $4}') / 1024 / 1024))
if [ $available_gb -lt 10 ]; then
echo -e "${RED}[✗] Insufficient disk space: ${available_gb}GB (minimum: 10GB)${NC}"
return 1
elif [ $available_gb -lt 20 ]; then
echo -e "${YELLOW}[!] Warning: 20GB free space is recommended (found: ${available_gb}GB)${NC}"
else
echo -e "${GREEN}[✓] Disk space: ${available_gb}GB available${NC}"
fi
local arch=$(uname -m)
if [[ "$arch" != "x86_64" && "$arch" != "aarch64" ]]; then
echo -e "${RED}[✗] Unsupported architecture: $arch${NC}"
return 1
fi
echo -e "${GREEN}[✓] Architecture: $arch${NC}"
# Check OS - CloudPanel supports Ubuntu 24.04, 22.04, Debian 12, 11
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu)
if [[ "$VERSION_ID" == "24.04" || "$VERSION_ID" == "22.04" ]]; then
echo -e "${GREEN}[✓] OS: $PRETTY_NAME${NC}"
else
echo -e "${YELLOW}[!] Warning: Ubuntu version $VERSION_ID may not be officially supported${NC}"
echo -e "${GRAY} Supported: Ubuntu 24.04 LTS, Ubuntu 22.04 LTS${NC}"
fi
;;
debian)
if [[ "$VERSION_ID" == "12" || "$VERSION_ID" == "11" ]]; then
echo -e "${GREEN}[✓] OS: $PRETTY_NAME${NC}"
else
echo -e "${YELLOW}[!] Warning: Debian version $VERSION_ID may not be officially supported${NC}"
echo -e "${GRAY} Supported: Debian 12 LTS, Debian 11 LTS${NC}"
fi
;;
*)
echo -e "${RED}[✗] Unsupported OS: $PRETTY_NAME${NC}"
echo -e "${GRAY} Supported: Ubuntu 24.04/22.04, Debian 12/11${NC}"
return 1
;;
esac
else
echo -e "${YELLOW}[!] Warning: Cannot detect OS version${NC}"
fi
return 0
}
get_clpctl_path() {
local max_attempts=30
local attempt=0
local clpctl_path=""
while [ $attempt -lt $max_attempts ]; do
if command -v clpctl &> /dev/null; then
clpctl_path="clpctl"
break
elif [ -f "/usr/local/bin/clpctl" ]; then
clpctl_path="/usr/local/bin/clpctl"
break
fi
attempt=$((attempt + 1))
sleep 2
done
echo "$clpctl_path"
}
configure_cloudpanel_basic_auth() {
echo ""
echo -e "${WHITE}${BOLD}Configuring CloudPanel Basic Auth...${NC}\n"
# Wait for clpctl to be available
echo -e "${PURPLE}[*] Waiting for CloudPanel CLI to be ready...${NC}"
local clpctl_path=$(get_clpctl_path)
if [ -z "$clpctl_path" ]; then
echo -e "${YELLOW}[!] Warning: CloudPanel CLI not found, skipping Basic Auth configuration${NC}"
return 1
fi
echo -e "${GREEN}[✓] CloudPanel CLI is ready${NC}"
echo ""
# Generate random credentials (global variables for display later)
BASIC_AUTH_USERNAME=$(generate_password 8)
BASIC_AUTH_PASSWORD=$(generate_password 20)
echo -e "${PURPLE}[*] Enabling Basic Auth...${NC}"
# Enable Basic Auth
if $clpctl_path cloudpanel:enable:basic-auth --userName="$BASIC_AUTH_USERNAME" --password="$BASIC_AUTH_PASSWORD" >> /tmp/lxs_cloudpanel.log 2>&1; then
echo -e "${GREEN}[✓] Basic Auth enabled successfully${NC}"
echo ""
return 0
else
echo -e "${YELLOW}[!] Warning: Failed to enable Basic Auth${NC}"
echo -e "${GRAY} You can enable it manually later with:${NC}"
echo -e "${GRAY} clpctl cloudpanel:enable:basic-auth --userName=USERNAME --password='PASSWORD'${NC}"
return 1
fi
}
create_cloudpanel_admin() {
echo ""
echo -e "${WHITE}${BOLD}Creating CloudPanel Admin Account...${NC}\n"
# Wait for clpctl to be available
echo -e "${PURPLE}[*] Waiting for CloudPanel CLI to be ready...${NC}"
local clpctl_path=$(get_clpctl_path)
if [ -z "$clpctl_path" ]; then
echo -e "${YELLOW}[!] Warning: CloudPanel CLI not found, skipping admin account creation${NC}"
return 1
fi
echo -e "${GREEN}[✓] CloudPanel CLI is ready${NC}"
echo ""
# Ask for admin email
echo -e "${CYAN}Admin Email (ex. admin@example.com):${NC}"
read -r ADMIN_EMAIL
while [ -z "$ADMIN_EMAIL" ]; do
echo -e "${RED}Email is required!${NC}"
read -r ADMIN_EMAIL
done
echo ""
# Generate random credentials (global variables for display later)
ADMIN_USERNAME=$(generate_password 8 | tr '[:upper:]' '[:lower:]')
ADMIN_PASSWORD=$(generate_password 20)
echo -e "${PURPLE}[*] Creating admin account...${NC}"
# Create admin user
if $clpctl_path user:add --userName="$ADMIN_USERNAME" --email="$ADMIN_EMAIL" --firstName="Admin" --lastName="User" --password="$ADMIN_PASSWORD" --role="admin" --timezone="UTC" --status="1" >> /tmp/lxs_cloudpanel.log 2>&1; then
echo -e "${GREEN}[✓] Admin account created successfully${NC}"
echo ""
return 0
else
echo -e "${YELLOW}[!] Warning: Failed to create admin account${NC}"
echo -e "${GRAY} You can create it manually later with:${NC}"
echo -e "${GRAY} clpctl user:add --userName=$ADMIN_USERNAME --email=$ADMIN_EMAIL --firstName='Admin' --lastName='User' --password='$ADMIN_PASSWORD' --role='admin' --timezone='UTC' --status='1'${NC}"
return 1
fi
}
# ═══════════════════════════════════════════════════════════════════════════
# Installation Functions
# ═══════════════════════════════════════════════════════════════════════════
install_cloudpanel() {
local start_time=$(date +%s)
apt_noninteractive
echo -e "${WHITE}${BOLD}CLOUDPANEL INSTALLATION${NC}\n"
if is_installed; then
echo -e "${YELLOW}CloudPanel is already installed!${NC}"
return 0
fi
check_requirements || return 1
echo ""
wait_for_apt || return 1
echo ""
# Update system and install required packages
echo -e "${PURPLE}[*] Updating system packages...${NC}"
if ! run_spinner "Updating system" apt update && apt -y upgrade && apt -y install curl wget sudo; then
echo -e "${RED}[✗] Failed to update system packages${NC}"
return 1
fi
echo ""
# Set database engine to MySQL 8.4
DB_ENGINE="MYSQL_8.4"
echo -e "${GREEN}[✓] Database engine: MySQL 8.4${NC}"
echo ""
# Download installer
echo -e "${PURPLE}[*] Downloading CloudPanel installer...${NC}"
if ! curl -sS "$INSTALLER_URL" -o /tmp/cloudpanel_install.sh; then
echo -e "${RED}[✗] Failed to download installer${NC}"
return 1
fi
# Verify SHA256 checksum
echo -e "${PURPLE}[*] Verifying installer checksum...${NC}"
local calculated_sha=$(sha256sum /tmp/cloudpanel_install.sh | awk '{print $1}')
if [ "$calculated_sha" != "$INSTALLER_SHA256" ]; then
echo -e "${RED}[✗] Installer checksum verification failed!${NC}"
echo -e "${GRAY} Expected: $INSTALLER_SHA256${NC}"
echo -e "${GRAY} Got: $calculated_sha${NC}"
rm -f /tmp/cloudpanel_install.sh
return 1
fi
echo -e "${GREEN}[✓] Installer checksum verified${NC}"
echo ""
# Get server IP
local server_ip=$(get_public_ip)
echo -e "${WHITE}Starting Installation...${NC}"
echo -e "${GRAY}[i] This may take 10-20 minutes...${NC}"
echo ""
# Execute installation
show_separator
DB_ENGINE=$DB_ENGINE bash /tmp/cloudpanel_install.sh
local install_exit_code=$?
show_separator
# Clean up installer
rm -f /tmp/cloudpanel_install.sh
if [ $install_exit_code -ne 0 ]; then
echo ""
echo -e "${RED}[✗] Installation failed!${NC}"
echo -e "${GRAY}Check /tmp/lxs_cloudpanel.log for details${NC}"
return 1
fi
echo -e "${GREEN}[✓] Installation completed${NC}"
# Configure Basic Auth
configure_cloudpanel_basic_auth
# Create admin account
create_cloudpanel_admin
local duration=$(( $(date +%s) - start_time ))
local minutes=$((duration / 60))
local seconds=$((duration % 60))
echo ""
echo -e "${GREEN}${BOLD}Installation Completed Successfully!${NC}"
echo -e "${GRAY}Installation time: ${minutes}m ${seconds}s${NC}"
echo ""
show_separator
echo -e "${WHITE}${BOLD}CLOUDPANEL ACCESS INFORMATION${NC}"
show_separator
echo -e "${WHITE}Panel URL: ${GREEN}${BOLD}https://$server_ip:$PANEL_PORT${NC}"
# Display Basic Auth credentials if configured
if [ -n "$BASIC_AUTH_USERNAME" ] && [ -n "$BASIC_AUTH_PASSWORD" ]; then
echo ""
echo -e "${WHITE}${BOLD}BASIC AUTH CREDENTIALS${NC}"
echo -e "${WHITE}Username: ${GREEN}${BOLD}$BASIC_AUTH_USERNAME${NC}"
echo -e "${WHITE}Password: ${GREEN}${BOLD}$BASIC_AUTH_PASSWORD${NC}"
echo ""
echo -e "${YELLOW}[!] Basic Auth is enabled. You will need these credentials to access CloudPanel.${NC}"
fi
# Display Admin account credentials if created
if [ -n "$ADMIN_USERNAME" ] && [ -n "$ADMIN_PASSWORD" ]; then
echo ""
echo -e "${WHITE}${BOLD}ADMIN ACCOUNT CREDENTIALS${NC}"
echo -e "${WHITE}Email: ${GREEN}${BOLD}$ADMIN_EMAIL${NC}"
echo -e "${WHITE}Username: ${GREEN}${BOLD}$ADMIN_USERNAME${NC}"
echo -e "${WHITE}Password: ${GREEN}${BOLD}$ADMIN_PASSWORD${NC}"
fi
echo ""
echo -e "${RED}${BOLD}IMPORTANT SECURITY STEPS:${NC}"
echo -e "${YELLOW}1. Access CloudPanel immediately via the URL above${NC}"
echo -e "${YELLOW}2. Ignore the self-signed certificate warning${NC}"
echo -e "${YELLOW}3. Click 'Advanced' and 'Proceed' to continue${NC}"
if [ -n "$BASIC_AUTH_USERNAME" ] && [ -n "$BASIC_AUTH_PASSWORD" ]; then
echo -e "${YELLOW}4. Enter Basic Auth credentials when prompted${NC}"
fi
if [ -n "$ADMIN_USERNAME" ] && [ -n "$ADMIN_PASSWORD" ]; then
echo -e "${YELLOW}$([ -n "$BASIC_AUTH_USERNAME" ] && echo "5" || echo "4"). Login with admin credentials above${NC}"
fi
echo ""
echo -e "${RED}${BOLD}REQUIRED FIREWALL PORTS:${NC}"
echo -e "${GRAY}20, 21, 22, 25, 53, 80, 443, 465, 587, 993, 995, $PANEL_PORT${NC}"
show_separator
if [ -n "$BASIC_AUTH_USERNAME" ] && [ -n "$BASIC_AUTH_PASSWORD" ]; then
echo ""
echo -e "${RED}${BOLD}IMPORTANT:${NC} ${YELLOW}Save Basic Auth credentials securely!${NC}"
fi
if [ -n "$ADMIN_USERNAME" ] && [ -n "$ADMIN_PASSWORD" ]; then
echo ""
echo -e "${RED}${BOLD}IMPORTANT:${NC} ${YELLOW}Save admin credentials securely!${NC}"
fi
}
update_cloudpanel() {
echo -e "${WHITE}${BOLD}CLOUDPANEL UPDATE${NC}\n"
apt_noninteractive
if ! is_installed; then
echo -e "${RED}CloudPanel is not installed!${NC}"
return 1
fi
echo -e "${PURPLE}[*] Updating CloudPanel...${NC}"
# Use clpctl if available
if command -v clpctl &> /dev/null; then
clpctl update
elif [ -f "/usr/local/bin/clpctl" ]; then
/usr/local/bin/clpctl update
else
echo -e "${YELLOW}[!] CloudPanel CLI not found, downloading latest installer...${NC}"
curl -sS "$INSTALLER_URL" -o /tmp/cloudpanel_update.sh
if echo "$INSTALLER_SHA256 /tmp/cloudpanel_update.sh" | sha256sum -c --quiet; then
bash /tmp/cloudpanel_update.sh
rm -f /tmp/cloudpanel_update.sh
else
echo -e "${RED}[✗] Failed to verify update installer${NC}"
rm -f /tmp/cloudpanel_update.sh
return 1
fi
fi
[ $? -eq 0 ] && echo -e "${GREEN}[✓] Update completed${NC}" || echo -e "${RED}[✗] Update failed${NC}"
}
show_status() {
echo -e "${WHITE}${BOLD}CLOUDPANEL STATUS${NC}\n"
if ! is_installed; then
echo -e "${RED}CloudPanel is not installed!${NC}"
return 1
fi
# Check CloudPanel service
if systemctl is-active --quiet cloudpanel; then
echo -e "${GREEN}[✓] CloudPanel service is running${NC}"
else
echo -e "${YELLOW}[!] CloudPanel service status unknown${NC}"
fi
# Check web server
if systemctl is-active --quiet nginx || systemctl is-active --quiet apache2; then
echo -e "${GREEN}[✓] Web server is running${NC}"
else
echo -e "${YELLOW}[!] Web server status unknown${NC}"
fi
# Check database
if systemctl is-active --quiet mysql || systemctl is-active --quiet mariadb; then
echo -e "${GREEN}[✓] Database service is running${NC}"
else
echo -e "${YELLOW}[!] Database service status unknown${NC}"
fi
echo ""
local server_ip=$(get_public_ip)
echo -e "${CYAN}Panel URL: ${BOLD}https://$server_ip:$PANEL_PORT${NC}"
# Show clpctl status if available
if command -v clpctl &> /dev/null || [ -f "/usr/local/bin/clpctl" ]; then
echo ""
echo -e "${WHITE}CloudPanel CLI available${NC}"
echo -e "${GRAY}Run 'clpctl' for available commands${NC}"
fi
return 0
}
install_rclone() {
echo -e "${PURPLE}[*] Checking rclone installation...${NC}"
if command -v rclone &> /dev/null; then
local rclone_version=$(rclone version | head -n 1 | awk '{print $2}')
echo -e "${GREEN}[✓] rclone is already installed (version: $rclone_version)${NC}"
return 0
fi
echo -e "${PURPLE}[*] Installing rclone...${NC}"
wait_for_apt || return 1
# Install rclone using official method
if ! run_spinner "Installing rclone" curl https://rclone.org/install.sh | bash; then
echo -e "${RED}[✗] Failed to install rclone${NC}"
return 1
fi
if command -v rclone &> /dev/null; then
local rclone_version=$(rclone version | head -n 1 | awk '{print $2}')
echo -e "${GREEN}[✓] rclone installed successfully (version: $rclone_version)${NC}"
return 0
else
echo -e "${RED}[✗] rclone installation failed${NC}"
return 1
fi
}
configure_rclone_backblaze() {
echo ""
echo -e "${WHITE}${BOLD}Configure rclone for Backblaze B2${NC}\n"
# Check if CloudPanel is installed
if ! is_installed; then
echo -e "${YELLOW}[!] CloudPanel is not installed. Please install CloudPanel first.${NC}"
return 1
fi
# Install rclone if needed
if ! install_rclone; then
return 1
fi
echo ""
echo -e "${CYAN}Backblaze B2 Configuration${NC}"
echo -e "${GRAY}You need to provide your Backblaze B2 credentials.${NC}"
echo -e "${GRAY}To get your credentials:${NC}"
echo -e "${GRAY} 1. Log in to your Backblaze account${NC}"
echo -e "${GRAY} 2. Go to 'App Keys' section${NC}"
echo -e "${GRAY} 3. Create a new Application Key (or use an existing one)${NC}"
echo -e "${GRAY} 4. Copy the 'Key ID' (Account ID) and 'Application Key'${NC}"
echo ""
# Ask for Account ID (Key ID)
echo -e "${CYAN}Account ID (Key ID):${NC}"
read -r B2_ACCOUNT_ID
while [ -z "$B2_ACCOUNT_ID" ]; do
echo -e "${RED}Account ID is required!${NC}"
read -r B2_ACCOUNT_ID
done
# Ask for Application Key
echo ""
echo -e "${CYAN}Application Key:${NC}"
read -rs B2_APPLICATION_KEY
while [ -z "$B2_APPLICATION_KEY" ]; do
echo -e "${RED}Application Key is required!${NC}"
read -rs B2_APPLICATION_KEY
done
echo ""
# Ask for bucket name (optional, for information)
echo ""
echo -e "${CYAN}Bucket Name (optional, for reference):${NC}"
read -r B2_BUCKET_NAME
echo ""
echo -e "${PURPLE}[*] Configuring rclone...${NC}"
# Create rclone config directory if it doesn't exist
# Use root's home directory since script runs as root
local rclone_config_dir="/root/.config/rclone"
mkdir -p "$rclone_config_dir"
# Configure rclone non-interactively
# The remote name must be "remote" for CloudPanel to recognize it
local rclone_config_file="$rclone_config_dir/rclone.conf"
# Check if remote already exists
if [ -f "$rclone_config_file" ] && grep -q "^\[remote\]" "$rclone_config_file" 2>/dev/null; then
echo -e "${YELLOW}[!] A remote named 'remote' already exists.${NC}"
echo -e "${CYAN}Do you want to overwrite it? (y/n):${NC}"
read -r overwrite_choice
if [[ ! "$overwrite_choice" =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}[!] Configuration cancelled${NC}"
return 1
fi
# Create a temporary file without the [remote] section
local temp_config=$(mktemp)
local in_remote_section=false
while IFS= read -r line || [ -n "$line" ]; do
if [[ "$line" =~ ^\[remote\] ]]; then
in_remote_section=true
continue
elif [[ "$line" =~ ^\[ ]] && [ "$in_remote_section" = true ]; then
in_remote_section=false
echo "$line" >> "$temp_config"
elif [ "$in_remote_section" = false ]; then
echo "$line" >> "$temp_config"
fi
done < "$rclone_config_file"
mv "$temp_config" "$rclone_config_file"
fi
# Add new remote configuration
cat >> "$rclone_config_file" << EOF
[remote]
type = b2
account = $B2_ACCOUNT_ID
key = $B2_APPLICATION_KEY
hard_delete = false
EOF
if [ $? -eq 0 ]; then
echo -e "${GREEN}[✓] rclone configured successfully${NC}"
else
echo -e "${RED}[✗] Failed to configure rclone${NC}"
return 1
fi
# Test the configuration
echo ""
echo -e "${PURPLE}[*] Testing rclone configuration...${NC}"
if rclone lsd remote: > /dev/null 2>&1; then
echo -e "${GREEN}[✓] rclone connection test successful${NC}"
# List buckets if available
echo ""
echo -e "${CYAN}Available buckets:${NC}"
rclone lsd remote: 2>/dev/null | awk '{print " - " $5}' || echo -e "${GRAY} (Unable to list buckets)${NC}"
else
echo -e "${YELLOW}[!] Warning: Could not test connection to Backblaze B2${NC}"
echo -e "${GRAY} Please verify your credentials are correct${NC}"
fi
echo ""
show_separator
echo -e "${WHITE}${BOLD}Configuration Complete!${NC}"
show_separator
echo ""
echo -e "${GREEN}[✓] rclone has been configured with Backblaze B2${NC}"
echo ""
echo -e "${WHITE}${BOLD}Next Steps:${NC}"
echo -e "${CYAN}1. Access CloudPanel at: ${BOLD}https://$(get_public_ip):$PANEL_PORT${NC}"
echo -e "${CYAN}2. Go to Settings > Backups${NC}"
echo -e "${CYAN}3. Select 'Custom Rclone Config' as the storage provider${NC}"
if [ -n "$B2_BUCKET_NAME" ]; then
echo -e "${CYAN}4. In 'Storage Directory', enter: ${BOLD}$B2_BUCKET_NAME${NC}"
else
echo -e "${CYAN}4. In 'Storage Directory', enter your Backblaze B2 bucket name${NC}"
fi
echo -e "${CYAN}5. Configure backup frequency and retention as needed${NC}"
echo -e "${CYAN}6. Save the configuration${NC}"
echo ""
echo -e "${GRAY}[i] Note: The rclone remote is named 'remote' as required by CloudPanel${NC}"
show_separator
}
# ═══════════════════════════════════════════════════════════════════════════
# Main Menu
# ═══════════════════════════════════════════════════════════════════════════
show_menu() {
clear
echo -e "${WHITE}${BOLD}CLOUDPANEL MANAGEMENT${NC}\n"
echo -e " ${GREEN}[1]${NC} Install CloudPanel"
echo -e " ${YELLOW}[2]${NC} Update CloudPanel"
echo -e " ${CYAN}[3]${NC} View Status"
echo -e " ${PURPLE}[4]${NC} Configure rclone for Backblaze B2"
echo -e " ${RED}[0]${NC} Back to main menu"
echo ""
echo -n "Choice [0-4]: "
}
main() {
while true; do
show_menu
read -r choice
echo ""
case $choice in
1) install_cloudpanel ;;
2) update_cloudpanel ;;
3) show_status ;;
4) configure_rclone_backblaze ;;
0) return 0 ;;
*) echo -e "${RED}Invalid option${NC}" ;;
esac
echo ""
read -p "Press Enter to continue..."
done
}
main
+260
View File
@@ -0,0 +1,260 @@
#!/bin/bash
# LXS - Coolify Installation Script
# Description: Install and manage Coolify deployment platform
# 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_coolify.log"
require_root "$0" "$@"
# ═══════════════════════════════════════════════════════════════════════════
# Configuration
# ═══════════════════════════════════════════════════════════════════════════
INSTALL_DIR="/data/coolify"
PORT=8000
# ═══════════════════════════════════════════════════════════════════════════
# Helper Functions
# ═══════════════════════════════════════════════════════════════════════════
is_installed() {
[ -d "$INSTALL_DIR" ] && docker ps | grep -q "coolify"
}
check_requirements() {
echo -e "${WHITE}Checking System Requirements...${NC}"
local cpu_cores=$(nproc)
[ $cpu_cores -lt 2 ] && echo -e "${YELLOW}[!] Warning: Recommended minimum is 2 CPU cores (found: $cpu_cores)${NC}" || echo -e "${GREEN}[✓] CPU cores: $cpu_cores${NC}"
local total_mem_gb=$(($(grep MemTotal /proc/meminfo | awk '{print $2}') / 1024 / 1024))
[ $total_mem_gb -lt 2 ] && echo -e "${YELLOW}[!] Warning: Recommended minimum is 2GB RAM (found: ${total_mem_gb}GB)${NC}" || echo -e "${GREEN}[✓] Memory: ${total_mem_gb}GB${NC}"
local available_gb=$(($(df / | awk 'NR==2 {print $4}') / 1024 / 1024))
[ $available_gb -lt 30 ] && echo -e "${YELLOW}[!] Warning: Recommended minimum is 30GB free space (found: ${available_gb}GB)${NC}" || echo -e "${GREEN}[✓] Disk space: ${available_gb}GB available${NC}"
local arch=$(uname -m)
if [[ "$arch" != "x86_64" && "$arch" != "aarch64" ]]; then
echo -e "${RED}[✗] Unsupported architecture: $arch${NC}"
return 1
fi
echo -e "${GREEN}[✓] Architecture: $arch${NC}"
return 0
}
# ═══════════════════════════════════════════════════════════════════════════
# Installation Functions
# ═══════════════════════════════════════════════════════════════════════════
install_coolify() {
local start_time=$(date +%s)
apt_noninteractive
echo -e "${WHITE}${BOLD}COOLIFY INSTALLATION${NC}\n"
if is_installed; then
echo -e "${YELLOW}Coolify is already installed!${NC}"
return 0
fi
check_requirements || return 1
echo ""
wait_for_apt || return 1
echo ""
echo -e "${WHITE}Running Official Coolify Installer${NC}"
echo -e "${GRAY}[i] Installing Docker and all dependencies...${NC}"
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
if [ $? -ne 0 ]; then
echo -e "${RED}[✗] Installation failed!${NC}"
return 1
fi
echo -e "${PURPLE}[*] Waiting for Coolify to start...${NC}"
sleep 10
local max_retries=30
local retry=0
while [ $retry -lt $max_retries ]; do
docker ps | grep -q "coolify" && break
retry=$((retry + 1))
sleep 2
done
local duration=$(( $(date +%s) - start_time ))
local minutes=$((duration / 60))
local seconds=$((duration % 60))
echo ""
echo -e "${GREEN}${BOLD}Installation Completed Successfully!${NC}"
echo -e "${GRAY}Installation time: ${minutes}m ${seconds}s${NC}"
echo ""
echo -e "${CYAN}Access URL: ${BOLD}http://$(get_public_ip):$PORT${NC}"
echo ""
echo -e "${RED}${BOLD}IMPORTANT:${NC} ${RED}Create your admin account immediately!${NC}"
echo -e "${RED}Visit the URL now to secure your installation.${NC}"
}
update_coolify() {
echo -e "${WHITE}${BOLD}COOLIFY UPDATE${NC}\n"
apt_noninteractive
if ! is_installed; then
echo -e "${RED}Coolify is not installed!${NC}"
return 1
fi
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
[ $? -eq 0 ] && echo -e "${GREEN}[✓] Update completed${NC}" || echo -e "${RED}[✗] Update failed${NC}"
}
show_status() {
echo -e "${WHITE}${BOLD}COOLIFY STATUS${NC}\n"
if ! is_installed; then
echo -e "${RED}Coolify is not installed!${NC}"
return 1
fi
if docker ps | grep -q "coolify"; then
echo -e "${GREEN}Coolify is running${NC}\n"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -E "(NAMES|coolify)"
echo ""
echo -e "${CYAN}Access URL: ${BOLD}http://$(get_public_ip):$PORT${NC}"
else
echo -e "${RED}Coolify is not running${NC}"
fi
return 0
}
# ═══════════════════════════════════════════════════════════════════════════
# Main Menu
# ═══════════════════════════════════════════════════════════════════════════
show_post_installation_guide() {
clear
echo -e "${WHITE}${BOLD}COOLIFY POST-INSTALLATION GUIDE${NC}\n"
show_separator
echo -e "${CYAN}${BOLD}1. Domain Name${NC}"
echo -e "${GRAY} Settings -> General${NC}"
echo -e "${WHITE} Name${NC}"
echo ""
show_separator
echo -e "${CYAN}${BOLD}2. Timezone${NC}"
echo -e "${GRAY} Settings -> General${NC}"
echo -e "${WHITE} Timezone: Toronto${NC}"
echo ""
show_separator
echo -e "${CYAN}${BOLD}3. Do Not Track Activation${NC}"
echo -e "${GRAY} Settings -> Advanced${NC}"
echo ""
show_separator
echo -e "${CYAN}${BOLD}4. Discord Notifications${NC}"
echo -e "${GRAY} Notifications -> Discord${NC}"
echo ""
echo -e "${WHITE} Configuration:${NC}"
echo -e " ${GREEN}[x]${NC} Deployment Success"
echo -e " ${GREEN}[x]${NC} Deployment Failure"
echo -e " ${GRAY}[ ]${NC} Container Status Changes"
echo -e " ${GREEN}[x]${NC} Backup Success"
echo -e " ${GREEN}[x]${NC} Backup Failure"
echo -e " ${GRAY}[ ]${NC} Scheduled Task Success"
echo -e " ${GREEN}[x]${NC} Scheduled Task Failure"
echo -e " ${GRAY}[ ]${NC} Docker Cleanup Success"
echo -e " ${GREEN}[x]${NC} Docker Cleanup Failure"
echo -e " ${GREEN}[x]${NC} Server Disk Usage"
echo -e " ${GREEN}[x]${NC} Server Reachable"
echo -e " ${GREEN}[x]${NC} Server Unreachable"
echo ""
show_separator
echo -e "${CYAN}${BOLD}5. S3 Storage for Backups${NC}"
echo -e "${GRAY} S3 Storages${NC}"
echo ""
echo -e "${WHITE} Configuration:${NC}"
echo -e " ${GRAY}Name:${NC} Provider name (ex. Backblaze)"
echo -e " ${GRAY}Endpoint:${NC} "
echo -e " ${GRAY}Bucket:${NC} "
echo -e " ${GRAY}Access Key:${NC} "
echo -e " ${GRAY}Secret Key:${NC} "
echo ""
show_separator
echo -e "${CYAN}${BOLD}6. Coolify Backup Configuration${NC}"
echo -e "${GRAY} Settings -> Backup${NC}"
echo ""
echo -e "${WHITE} Configuration:${NC}"
echo -e " ${GREEN}[x]${NC} S3 Enabled"
echo -e " ${GRAY}Number of backups to keep:${NC} 7"
echo ""
show_separator
echo -e "${CYAN}${BOLD}7. Server Timezone${NC}"
echo -e "${GRAY} Servers -> localhost -> General${NC}"
echo -e "${WHITE} Timezone: Toronto${NC}"
echo ""
show_separator
echo -e "${CYAN}${BOLD}8. Docker Cleanup Schedule${NC}"
echo -e "${GRAY} Servers -> localhost -> Docker Cleanup${NC}"
echo ""
echo -e "${WHITE} Schedule (every hour):${NC}"
echo -e " ${GRAY}0 * * * *${NC}"
echo ""
show_separator
echo ""
}
show_menu() {
clear
echo -e "${WHITE}${BOLD}COOLIFY MANAGEMENT${NC}\n"
echo -e " ${GREEN}[1]${NC} Install Coolify"
echo -e " ${YELLOW}[2]${NC} Update Coolify"
echo -e " ${CYAN}[3]${NC} View Status"
echo -e " ${PURPLE}[4]${NC} Post-Installation Guide"
echo -e " ${RED}[0]${NC} Back to main menu"
echo ""
echo -n "Choice [0-4]: "
}
main() {
while true; do
show_menu
read -r choice
echo ""
case $choice in
1) install_coolify ;;
2) update_coolify ;;
3) show_status ;;
4) show_post_installation_guide ;;
0) return 0 ;;
*) echo -e "${RED}Invalid option${NC}" ;;
esac
echo ""
read -p "Press Enter to continue..."
done
}
main
+354
View File
@@ -0,0 +1,354 @@
#!/bin/bash
# LXS - Proxmox VE Management Script
# Description: Tools for managing and troubleshooting Proxmox VE
# 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_proxmox.log"
require_root "$0" "$@"
# ═══════════════════════════════════════════════════════════════════════════
# Configuration
# ═══════════════════════════════════════════════════════════════════════════
LOCKFILE="/var/lib/pve-cluster/.pmxcfs.lockfile"
SERVICE_NAME="pve-cluster"
PANEL_PORT=8006
# ═══════════════════════════════════════════════════════════════════════════
# Helper Functions
# ═══════════════════════════════════════════════════════════════════════════
is_proxmox() {
[ -f /etc/pve/.version ] || command -v pveversion &> /dev/null
}
get_service_status() {
if systemctl is-active --quiet $SERVICE_NAME; then
echo -e "${GREEN}running${NC}"
else
echo -e "${RED}stopped${NC}"
fi
}
check_proxmox() {
if ! is_proxmox; then
echo -e "${RED}[!] This is not a Proxmox VE system!${NC}"
echo -e "${YELLOW} This script is designed for Proxmox VE only.${NC}"
return 1
fi
return 0
}
# ═══════════════════════════════════════════════════════════════════════════
# Login Fix Functions
# ═══════════════════════════════════════════════════════════════════════════
fix_login_issue() {
local start_time=$(date +%s)
echo -e "${WHITE}${BOLD}PROXMOX LOGIN FIX${NC}\n"
check_proxmox || return 1
echo -e "${CYAN}This will reset the pve-cluster lockfile to fix login issues.${NC}"
echo ""
echo "[1/3] Stopping pve-cluster service..."
run_spinner "Stopping $SERVICE_NAME..." "systemctl stop $SERVICE_NAME"
if [ $? -ne 0 ]; then
echo -e "${RED}[!] Failed to stop pve-cluster service${NC}"
return 1
fi
echo ""
echo "[2/3] Removing lockfile..."
if [ -f "$LOCKFILE" ]; then
run_spinner "Removing lockfile..." "rm -f '$LOCKFILE'"
echo -e "${GRAY} Lockfile was present and has been removed${NC}"
else
echo -e "${YELLOW}[!] Lockfile not found (may already be removed)${NC}"
fi
echo ""
echo "[3/3] Starting pve-cluster service..."
run_spinner "Starting $SERVICE_NAME..." "systemctl start $SERVICE_NAME"
if [ $? -ne 0 ]; then
echo -e "${RED}[!] Failed to start pve-cluster service${NC}"
echo -e "${YELLOW} Check logs with: journalctl -xe${NC}"
return 1
fi
sleep 2
local duration=$(( $(date +%s) - start_time ))
local server_ip=$(hostname -I | awk '{print $1}')
echo ""
show_separator
echo -e "${GREEN}${BOLD}Fix Completed!${NC}"
echo -e "${GRAY}Time: ${duration}s${NC}"
echo ""
echo -e "${WHITE}Service Status:${NC} $(get_service_status)"
echo ""
echo -e "${CYAN}You should now be able to login to the Proxmox web interface.${NC}"
echo -e "${WHITE}Panel URL: ${GREEN}${BOLD}https://$server_ip:$PANEL_PORT${NC}"
show_separator
}
# ═══════════════════════════════════════════════════════════════════════════
# Service Management Functions
# ═══════════════════════════════════════════════════════════════════════════
restart_pve_cluster() {
echo -e "${WHITE}${BOLD}RESTART PVE-CLUSTER${NC}\n"
check_proxmox || return 1
run_spinner "Restarting $SERVICE_NAME..." "systemctl restart $SERVICE_NAME"
sleep 2
echo ""
echo -e "${WHITE}Service Status:${NC} $(get_service_status)"
}
restart_all_services() {
echo -e "${WHITE}${BOLD}RESTART ALL PROXMOX SERVICES${NC}\n"
check_proxmox || return 1
local services=("pve-cluster" "pvedaemon" "pveproxy" "pvestatd")
for service in "${services[@]}"; do
if systemctl list-unit-files | grep -q "^$service.service"; then
run_spinner "Restarting $service..." "systemctl restart $service"
fi
done
sleep 2
echo ""
echo -e "${GREEN}[✓] All services restarted${NC}"
echo ""
echo -e "${WHITE}Panel URL: ${GREEN}${BOLD}https://$(hostname -I | awk '{print $1}'):$PANEL_PORT${NC}"
}
# ═══════════════════════════════════════════════════════════════════════════
# Status Functions
# ═══════════════════════════════════════════════════════════════════════════
show_status() {
echo -e "${WHITE}${BOLD}PROXMOX VE STATUS${NC}\n"
check_proxmox || return 1
# Proxmox version
echo -e "${WHITE}Proxmox Version:${NC}"
pveversion 2>/dev/null || echo -e "${GRAY}Unable to determine version${NC}"
echo ""
# Service statuses
echo -e "${WHITE}Service Status:${NC}"
local services=("pve-cluster" "pvedaemon" "pveproxy" "pvestatd")
for service in "${services[@]}"; do
if systemctl list-unit-files | grep -q "^$service.service"; then
if systemctl is-active --quiet $service; then
echo -e " ${GREEN}[✓]${NC} $service"
else
echo -e " ${RED}[✗]${NC} $service"
fi
fi
done
echo ""
# Lockfile status
if [ -f "$LOCKFILE" ]; then
echo -e "${WHITE}Lockfile:${NC} ${YELLOW}Present${NC} ($LOCKFILE)"
else
echo -e "${WHITE}Lockfile:${NC} ${GREEN}Not present${NC}"
fi
echo ""
# Access URL
local server_ip=$(hostname -I | awk '{print $1}')
echo -e "${WHITE}Panel URL: ${GREEN}${BOLD}https://$server_ip:$PANEL_PORT${NC}"
echo ""
# Cluster info
if command -v pvecm &> /dev/null; then
echo -e "${WHITE}Cluster Status:${NC}"
pvecm status 2>/dev/null | head -5 || echo -e "${GRAY}Not in a cluster or unable to get status${NC}"
fi
return 0
}
show_storage_status() {
echo -e "${WHITE}${BOLD}PROXMOX STORAGE STATUS${NC}\n"
check_proxmox || return 1
# Storage list
echo -e "${WHITE}Storage Configuration:${NC}"
if command -v pvesm &> /dev/null; then
pvesm status 2>/dev/null || echo -e "${GRAY}Unable to get storage status${NC}"
else
echo -e "${GRAY}pvesm command not available${NC}"
fi
echo ""
# Disk usage
echo -e "${WHITE}Disk Usage:${NC}"
df -h / /var /tmp 2>/dev/null | head -5
return 0
}
# ═══════════════════════════════════════════════════════════════════════════
# Maintenance Functions
# ═══════════════════════════════════════════════════════════════════════════
clear_cache() {
echo -e "${WHITE}${BOLD}CLEAR PROXMOX CACHE${NC}\n"
check_proxmox || return 1
echo -e "${CYAN}This will clear various Proxmox caches and temporary files.${NC}"
echo ""
run_spinner "Clearing apt cache..." "apt clean"
run_spinner "Clearing journal logs (older than 7 days)..." "journalctl --vacuum-time=7d"
# Clear task logs older than 30 days
if [ -d "/var/log/pve/tasks" ]; then
run_spinner "Clearing old task logs..." "find /var/log/pve/tasks -type f -mtime +30 -delete"
fi
echo ""
echo -e "${GREEN}[✓] Cache cleared${NC}"
}
update_proxmox() {
echo -e "${WHITE}${BOLD}UPDATE PROXMOX VE${NC}\n"
check_proxmox || return 1
# Configure non-interactive mode
export DEBIAN_FRONTEND=noninteractive
export NEEDRESTART_MODE=a
export NEEDRESTART_SUSPEND=1
local start_time=$(date +%s)
run_spinner "Updating package lists..." "apt update"
echo ""
echo -e "${PURPLE}[*] Upgrading packages...${NC}"
show_separator
apt dist-upgrade -y
local exit_code=$?
show_separator
if [ $exit_code -eq 0 ]; then
local duration=$(( $(date +%s) - start_time ))
echo ""
echo -e "${GREEN}${BOLD}Update Completed!${NC}"
echo -e "${GRAY}Time: $((duration / 60))m $((duration % 60))s${NC}"
echo ""
echo -e "${YELLOW}[!] A reboot may be required if the kernel was updated.${NC}"
else
echo ""
echo -e "${RED}[✗] Update failed${NC}"
return 1
fi
}
# ═══════════════════════════════════════════════════════════════════════════
# Network Functions
# ═══════════════════════════════════════════════════════════════════════════
show_network_info() {
echo -e "${WHITE}${BOLD}PROXMOX NETWORK INFO${NC}\n"
check_proxmox || return 1
local server_ip=$(hostname -I | awk '{print $1}')
local public_ip=$(get_public_ip)
echo -e "${WHITE}Hostname:${NC} $(hostname)"
echo -e "${WHITE}Local IP:${NC} $server_ip"
echo -e "${WHITE}Public IP:${NC} $public_ip"
echo ""
echo -e "${WHITE}Network Interfaces:${NC}"
ip -br addr show 2>/dev/null || ifconfig 2>/dev/null | grep -E "^[a-z]|inet "
echo ""
echo -e "${WHITE}Bridges:${NC}"
if command -v brctl &> /dev/null; then
brctl show 2>/dev/null || echo -e "${GRAY}No bridges found${NC}"
else
ip link show type bridge 2>/dev/null || echo -e "${GRAY}No bridges found${NC}"
fi
return 0
}
# ═══════════════════════════════════════════════════════════════════════════
# Main Menu
# ═══════════════════════════════════════════════════════════════════════════
show_menu() {
clear
echo -e "${WHITE}${BOLD}PROXMOX VE MANAGEMENT${NC}\n"
echo -e " ${GREEN}[1]${NC} Fix Login Issue (reset lockfile)"
echo -e " ${YELLOW}[2]${NC} Restart pve-cluster service"
echo -e " ${YELLOW}[3]${NC} Restart all Proxmox services"
echo -e " ${CYAN}[4]${NC} View System Status"
echo -e " ${CYAN}[5]${NC} View Storage Status"
echo -e " ${CYAN}[6]${NC} View Network Info"
echo -e " ${PURPLE}[7]${NC} Update Proxmox VE"
echo -e " ${PURPLE}[8]${NC} Clear Cache"
echo -e " ${RED}[0]${NC} Back to main menu"
echo ""
echo -n "Choice [0-8]: "
}
main() {
while true; do
show_menu
read -r choice
echo ""
case $choice in
1) fix_login_issue ;;
2) restart_pve_cluster ;;
3) restart_all_services ;;
4) show_status ;;
5) show_storage_status ;;
6) show_network_info ;;
7) update_proxmox ;;
8) clear_cache ;;
0) return 0 ;;
*) echo -e "${RED}Invalid option${NC}" ;;
esac
echo ""
read -p "Press Enter to continue..."
done
}
main
+330
View File
@@ -0,0 +1,330 @@
#!/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
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
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 0 ;;
*) echo -e "${RED}Invalid option${NC}" ;;
esac
echo ""
read -p "Press Enter to continue..."
done
}
main
+248
View File
@@ -0,0 +1,248 @@
#!/bin/bash
# LXS - Uptime Kuma Installation Script
# Description: Install and manage Uptime Kuma monitoring tool
# 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_uptime_kuma.log"
require_root "$0" "$@"
# ═══════════════════════════════════════════════════════════════════════════
# Configuration
# ═══════════════════════════════════════════════════════════════════════════
INSTALL_DIR="/opt/uptime-kuma"
SERVICE_NAME="uptime-kuma"
PORT=3001
# ═══════════════════════════════════════════════════════════════════════════
# Helper Functions
# ═══════════════════════════════════════════════════════════════════════════
is_installed() {
[ -d "$INSTALL_DIR" ] && systemctl list-unit-files | grep -q "$SERVICE_NAME.service"
}
# ═══════════════════════════════════════════════════════════════════════════
# Installation Functions
# ═══════════════════════════════════════════════════════════════════════════
install_dependencies() {
apt_noninteractive
run_spinner "Updating system..." "apt update -qq && apt upgrade -y -qq -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'"
run_spinner "Installing dependencies..." "apt install -y -qq curl git wget -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'"
run_spinner "Adding Node.js repository..." "curl -fsSL https://deb.nodesource.com/setup_20.x | bash -"
run_spinner "Installing Node.js..." "apt install -y -qq nodejs -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'"
}
configure_domain_ssl() {
local domain=$1
local email=$2
run_spinner "Installing Nginx..." "apt install -y -qq nginx -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'"
run_spinner "Installing Certbot..." "apt install -y -qq certbot python3-certbot-nginx -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'"
cat > /etc/nginx/sites-available/$domain <<EOF
server {
listen 80;
server_name $domain;
location / {
proxy_pass http://localhost:$PORT;
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;
}
}
EOF
ln -sf /etc/nginx/sites-available/$domain /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl restart nginx
certbot --nginx -d $domain --non-interactive --agree-tos --email $email --redirect 2>/dev/null
}
# ═══════════════════════════════════════════════════════════════════════════
# Main Installation
# ═══════════════════════════════════════════════════════════════════════════
install_uptime_kuma() {
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}UPTIME KUMA INSTALLATION${NC}\n"
if is_installed; then
echo -e "${YELLOW}Uptime Kuma is already installed!${NC}"
return 0
fi
echo "[1/4] Installing dependencies..."
install_dependencies
echo ""
echo "[2/4] Cloning repository..."
run_spinner "Downloading from GitHub..." "git clone https://github.com/louislam/uptime-kuma.git '$INSTALL_DIR'"
echo ""
echo "[3/4] Installing Uptime Kuma..."
cd "$INSTALL_DIR"
run_spinner "Running npm setup..." "npm run setup"
echo ""
echo "[4/4] Creating service..."
cat > /etc/systemd/system/$SERVICE_NAME.service <<EOF
[Unit]
Description=Uptime Kuma
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=$INSTALL_DIR
ExecStart=/usr/bin/npm run start-server
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
EOF
run_spinner "Starting service..." "systemctl daemon-reload && systemctl enable $SERVICE_NAME && systemctl start $SERVICE_NAME"
sleep 5
local duration=$(( $(date +%s) - start_time ))
local server_ip=$(get_public_ip)
echo ""
echo -e "${GREEN}${BOLD}Installation Completed!${NC}"
echo -e "${GRAY}Time: $((duration / 60))m $((duration % 60))s${NC}"
echo ""
echo -e "${CYAN}Access URL: ${BOLD}http://$server_ip:$PORT${NC}"
echo ""
# Optional domain configuration
echo -e "${WHITE}Configure domain with SSL? (y/n)${NC}"
read -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
read -p "Domain name: " DOMAIN_NAME
read -p "Email for SSL: " EMAIL
echo ""
configure_domain_ssl "$DOMAIN_NAME" "$EMAIL"
[ $? -eq 0 ] && echo -e "${GREEN}[✓] Domain configured: ${BOLD}https://$DOMAIN_NAME${NC}"
fi
echo ""
echo -e "${WHITE}Next steps:${NC}"
echo -e "1. Visit the URL above"
echo -e "2. Choose SQLite database"
echo -e "3. Create admin account"
}
update_uptime_kuma() {
echo -e "${WHITE}${BOLD}UPTIME KUMA UPDATE${NC}\n"
# Configure non-interactive mode
export DEBIAN_FRONTEND=noninteractive
export NEEDRESTART_MODE=a
export NEEDRESTART_SUSPEND=1
if ! is_installed; then
echo -e "${RED}Uptime Kuma is not installed!${NC}"
return 1
fi
local start_time=$(date +%s)
run_spinner "Stopping service..." "systemctl stop $SERVICE_NAME"
BACKUP_DIR="${INSTALL_DIR}_backup_$(date +%Y%m%d_%H%M%S)"
[ -d "$INSTALL_DIR/data" ] && run_spinner "Backing up..." "cp -r '$INSTALL_DIR/data' '$BACKUP_DIR'"
cd "$INSTALL_DIR"
run_spinner "Pulling updates..." "git fetch --all && git checkout master && git pull"
run_spinner "Updating dependencies..." "npm run setup"
run_spinner "Starting service..." "systemctl start $SERVICE_NAME"
local duration=$(( $(date +%s) - start_time ))
echo ""
echo -e "${GREEN}${BOLD}Update Completed!${NC}"
echo -e "${GRAY}Time: $((duration / 60))m $((duration % 60))s${NC}"
echo -e "${CYAN}Access: ${BOLD}http://$(get_public_ip):$PORT${NC}"
}
show_status() {
echo -e "${WHITE}${BOLD}UPTIME KUMA STATUS${NC}\n"
if ! is_installed; then
echo -e "${RED}Uptime Kuma is not installed!${NC}"
return 1
fi
if systemctl is-active --quiet $SERVICE_NAME; then
echo -e "${GREEN}Service is running${NC}\n"
systemctl status $SERVICE_NAME --no-pager | head -10
echo ""
echo -e "${CYAN}Access: ${BOLD}http://$(get_public_ip):$PORT${NC}"
else
echo -e "${RED}Service is not running${NC}"
fi
return 0
}
# ═══════════════════════════════════════════════════════════════════════════
# Main Menu
# ═══════════════════════════════════════════════════════════════════════════
show_menu() {
clear
echo -e "${WHITE}${BOLD}UPTIME KUMA MANAGEMENT${NC}\n"
echo -e " ${GREEN}[1]${NC} Install Uptime Kuma"
echo -e " ${YELLOW}[2]${NC} Update Uptime Kuma"
echo -e " ${CYAN}[3]${NC} View Status"
echo -e " ${RED}[0]${NC} Back to main menu"
echo ""
echo -n "Choice [0-3]: "
}
main() {
while true; do
show_menu
read -r choice
echo ""
case $choice in
1) install_uptime_kuma ;;
2) update_uptime_kuma ;;
3) show_status ;;
0) return 0 ;;
*) echo -e "${RED}Invalid option${NC}" ;;
esac
echo ""
read -p "Press Enter to continue..."
done
}
main