From d2ab069c347f6a023359bf1f1a6ddf9c0085de39 Mon Sep 17 00:00:00 2001 From: master3395 Date: Wed, 4 Feb 2026 20:31:28 +0100 Subject: [PATCH 01/16] Install: MariaDB prompt first, non-interactive SSH key, AlmaLinux 9 MariaDB 11.8 - cyberpanel.sh: ask MariaDB version (11.8/12.1) first even in --auto; add --mariadb-version - install/install.py: remove existing SSH key before ssh-keygen to avoid Overwrite (y/n) prompt - install/installCyberPanel.py: use MariaDB 11.8 and --nobest for AlmaLinux 9 / cent8 to fix client dependency --- cyberpanel.sh | 50 +++++++++++++++++++++++++++++++----- install/install.py | 21 +++++++++++---- install/installCyberPanel.py | 15 ++++++----- 3 files changed, 68 insertions(+), 18 deletions(-) diff --git a/cyberpanel.sh b/cyberpanel.sh index 0748c7212..557154c3a 100644 --- a/cyberpanel.sh +++ b/cyberpanel.sh @@ -11,6 +11,7 @@ OS_FAMILY="" PACKAGE_MANAGER="" ARCHITECTURE="" BRANCH_NAME="" +MARIADB_VER="" DEBUG_MODE=false AUTO_INSTALL=false INSTALLATION_TYPE="" @@ -579,6 +580,20 @@ cleanup_existing_cyberpanel() { # Function to install CyberPanel directly using the working method install_cyberpanel_direct() { + # Ask MariaDB version first (even in no-confirmation/auto mode) if not set via --mariadb-version + if [ -z "$MARIADB_VER" ]; then + echo "" + echo " MariaDB version: 11.8 (LTS, default) or 12.1?" + read -r -t 60 -p " Enter 11.8 or 12.1 [11.8]: " MARIADB_VER || true + MARIADB_VER="${MARIADB_VER:-11.8}" + MARIADB_VER="${MARIADB_VER// /}" + if [ "$MARIADB_VER" != "11.8" ] && [ "$MARIADB_VER" != "12.1" ]; then + MARIADB_VER="11.8" + fi + echo " Using MariaDB $MARIADB_VER" + echo "" + fi + echo " 🔄 Downloading CyberPanel installation files..." # Check if CyberPanel is already installed @@ -1185,6 +1200,7 @@ except Exception as e: install_args+=("--powerdns" "ON") install_args+=("--ftp" "ON") install_args+=("--remotemysql" "OFF") + install_args+=("--mariadb-version" "${MARIADB_VER:-11.8}") if [ "$DEBUG_MODE" = true ]; then # Note: install.py doesn't have --debug, but we can set it via environment @@ -1352,8 +1368,9 @@ apply_fixes() { systemctl start mariadb 2>/dev/null || true systemctl enable mariadb 2>/dev/null || true - # Fix LiteSpeed service - cat > /etc/systemd/system/lsws.service << 'EOF' + # Fix LiteSpeed service only if the web server was actually installed + if [ -x /usr/local/lsws/bin/lswsctrl ] || [ -x /usr/local/lsws/bin/lsctrl ] || [ -f /usr/local/lsws/bin/openlitespeed ]; then + cat > /etc/systemd/system/lsws.service << 'EOF' [Unit] Description=LiteSpeed Web Server After=network.target @@ -1372,9 +1389,15 @@ RestartSec=5 WantedBy=multi-user.target EOF - systemctl daemon-reload - systemctl enable lsws - systemctl start lsws + systemctl daemon-reload + systemctl enable lsws + systemctl start lsws || true + else + echo " • LiteSpeed/OpenLiteSpeed not found at /usr/local/lsws - skipping lsws.service (install may have skipped web server)" + systemctl disable lsws 2>/dev/null || true + rm -f /etc/systemd/system/lsws.service + systemctl daemon-reload + fi # Set OpenLiteSpeed admin password to match CyberPanel echo " • Configuring OpenLiteSpeed admin password..." @@ -2674,6 +2697,18 @@ parse_arguments() { set -x shift ;; + --mariadb-version) + if [ -n "$2" ] && [ "$2" = "11.8" ]; then + MARIADB_VER="11.8" + shift 2 + elif [ -n "$2" ] && [ "$2" = "12.1" ]; then + MARIADB_VER="12.1" + shift 2 + else + echo "ERROR: --mariadb-version requires 11.8 or 12.1" + exit 1 + fi + ;; --auto) AUTO_INSTALL=true shift @@ -2683,8 +2718,9 @@ parse_arguments() { echo "Options:" echo " -b, --branch BRANCH Install from specific branch/commit" echo " -v, --version VER Install specific version (auto-adds v prefix)" + echo " --mariadb-version VER MariaDB version: 11.8 or 12.1 (asked first if omitted)" echo " --debug Enable debug mode" - echo " --auto Auto mode without prompts" + echo " --auto Auto mode without prompts (MariaDB still asked first unless --mariadb-version)" echo " -h, --help Show this help message" echo "" echo "Examples:" @@ -2696,6 +2732,8 @@ parse_arguments() { echo " $0 -v 2.4.3 # Install version 2.4.3" echo " $0 -b main # Install from main branch" echo " $0 -b a1b2c3d4 # Install from specific commit" + echo " $0 --mariadb-version 12.1 # Use MariaDB 12.1 (no prompt)" + echo " $0 --auto --mariadb-version 11.8 # Fully non-interactive with MariaDB 11.8" echo "" echo "Standard CyberPanel Installation Methods:" echo " sh <(curl https://cyberpanel.net/install.sh)" diff --git a/install/install.py b/install/install.py index ac0224a1e..0c29f9c8e 100644 --- a/install/install.py +++ b/install/install.py @@ -3307,10 +3307,10 @@ password="%s" preFlightsChecks.stdOut("ERROR: Virtual environment setup failed!", 0) return False - # Find the correct Python virtual environment path + # Find the correct Python virtual environment path (prefer CyberCP - app install path) python_paths = [ - "/usr/local/CyberPanel/bin/python", "/usr/local/CyberCP/bin/python", + "/usr/local/CyberPanel/bin/python", "/usr/local/CyberPanel-venv/bin/python" ] @@ -4956,8 +4956,8 @@ user_query = SELECT email as user, password, 'vmail' as uid, 'vmail' as gid, '/h # Determine the correct Python path python_paths = [ - "/usr/local/CyberPanel/bin/python", "/usr/local/CyberCP/bin/python", + "/usr/local/CyberPanel/bin/python", "/usr/bin/python3", "/usr/local/bin/python3" ] @@ -5026,7 +5026,7 @@ user_query = SELECT email as user, password, 'vmail' as uid, 'vmail' as gid, '/h def ensureVirtualEnvironmentSetup(self): """Ensure virtual environment is properly set up and accessible""" try: - # Check multiple possible virtual environment locations + # Check multiple possible virtual environment locations (prefer CyberCP - app path) venv_paths = [ '/usr/local/CyberCP/bin/python', '/usr/local/CyberPanel/bin/python', @@ -5077,8 +5077,8 @@ user_query = SELECT email as user, password, 'vmail' as uid, 'vmail' as gid, '/h # Determine the correct Python path python_paths = [ - "/usr/local/CyberPanel/bin/python", "/usr/local/CyberCP/bin/python", + "/usr/local/CyberPanel/bin/python", "/usr/bin/python3", "/usr/local/bin/python3" ] @@ -5292,10 +5292,21 @@ user_query = SELECT email as user, password, 'vmail' as uid, 'vmail' as gid, '/h def install_default_keys(self): try: path = "/root/.ssh" + key_path = "/root/.ssh/cyberpanel" + key_pub = "/root/.ssh/cyberpanel.pub" if not os.path.exists(path): os.mkdir(path) + # Remove existing key files so ssh-keygen never prompts "Overwrite (y/n)?" + # This keeps unattended / no-confirmation installs fully non-interactive + for f in (key_path, key_pub): + if os.path.exists(f): + try: + os.remove(f) + except OSError: + pass + command = "ssh-keygen -f /root/.ssh/cyberpanel -t rsa -N ''" preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) diff --git a/install/installCyberPanel.py b/install/installCyberPanel.py index 1ff6cb0d6..1e0aed194 100644 --- a/install/installCyberPanel.py +++ b/install/installCyberPanel.py @@ -85,15 +85,15 @@ class InstallCyberPanel: command = "dnf clean all" install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - # Install MariaDB from official repository - self.stdOut("Setting up official MariaDB repository...", 1) - command = "curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | bash -s -- --mariadb-server-version='10.11'" + # Install MariaDB from official repository (11.8 LTS for el9 to avoid client dependency issues) + self.stdOut("Setting up official MariaDB repository (11.8 LTS)...", 1) + command = "curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | bash -s -- --mariadb-server-version='11.8'" install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - # Install MariaDB packages + # Install MariaDB packages (server + client from same repo to satisfy dependencies) self.stdOut("Installing MariaDB packages...", 1) mariadb_packages = "MariaDB-server MariaDB-client MariaDB-backup MariaDB-devel" - command = f"dnf install -y {mariadb_packages}" + command = f"dnf install -y --nobest {mariadb_packages}" install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) self.stdOut("AlmaLinux 9 MariaDB fixes completed", 1) @@ -902,7 +902,7 @@ gpgcheck=1 else: - command = 'curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version=10.11' + command = 'curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version=11.8' install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) command = 'yum remove mariadb* -y' @@ -922,7 +922,8 @@ gpgcheck=1 command = 'dnf clean all' install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - command = 'dnf install MariaDB-server MariaDB-client MariaDB-backup -y' + # Use --nobest so server+client resolve from same repo (avoids AlmaLinux 9 dependency conflict) + command = 'dnf install -y --nobest MariaDB-server MariaDB-client MariaDB-backup' install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) From 59894fc165ddd0d8ae4133bb2ea476e5524f4a62 Mon Sep 17 00:00:00 2001 From: master3395 Date: Wed, 4 Feb 2026 21:15:10 +0100 Subject: [PATCH 02/16] Add MariaDB 10.11 option; fix install/upgrade robustness - install.py: allow --mariadb-version 10.11 (with 11.8, 12.1); use --nobest for 10.11/11.8 on RHEL; accept existing 10.x - cyberpanel.sh: prompt and --mariadb-version accept 10.11; only pass --mariadb-version if install.py supports it; fix MySQLdb check (no __version__) - cyberpanel_upgrade.sh: accept 10.11 in --mariadb-version and prompt; Check_Root allow uid 0 when SUDO set; Branch_Check avoid double v (vv2.5.5-dev); early root check --- cyberpanel.sh | 27 ++++++++++++++-------- cyberpanel_upgrade.sh | 53 ++++++++++++++++++++++++++++++------------- install/install.py | 19 +++++++++------- 3 files changed, 65 insertions(+), 34 deletions(-) diff --git a/cyberpanel.sh b/cyberpanel.sh index 557154c3a..4adde917a 100644 --- a/cyberpanel.sh +++ b/cyberpanel.sh @@ -583,11 +583,11 @@ install_cyberpanel_direct() { # Ask MariaDB version first (even in no-confirmation/auto mode) if not set via --mariadb-version if [ -z "$MARIADB_VER" ]; then echo "" - echo " MariaDB version: 11.8 (LTS, default) or 12.1?" - read -r -t 60 -p " Enter 11.8 or 12.1 [11.8]: " MARIADB_VER || true + echo " MariaDB version: 10.11, 11.8 (LTS, default) or 12.1?" + read -r -t 60 -p " Enter 10.11, 11.8 or 12.1 [11.8]: " MARIADB_VER || true MARIADB_VER="${MARIADB_VER:-11.8}" MARIADB_VER="${MARIADB_VER// /}" - if [ "$MARIADB_VER" != "11.8" ] && [ "$MARIADB_VER" != "12.1" ]; then + if [ "$MARIADB_VER" != "10.11" ] && [ "$MARIADB_VER" != "11.8" ] && [ "$MARIADB_VER" != "12.1" ]; then MARIADB_VER="11.8" fi echo " Using MariaDB $MARIADB_VER" @@ -1176,9 +1176,10 @@ except Exception as e: fi fi - # Verify MySQLdb is available + # Verify MySQLdb is available (mysqlclient; some builds lack __version__) print_status "Verifying MySQLdb module availability..." - if python3 -c "import MySQLdb; print('MySQLdb version:', MySQLdb.__version__)" 2>&1; then + if python3 -c "import MySQLdb; getattr(MySQLdb, '__version__', 'ok'); print('MySQLdb OK')" 2>/dev/null || \ + python3 -c "import MySQLdb; MySQLdb; print('MySQLdb OK')" 2>/dev/null; then print_status "✓ MySQLdb module is available and working" else print_status "⚠️ WARNING: MySQLdb module not available" @@ -1195,12 +1196,14 @@ except Exception as e: # Add optional arguments based on user preferences # Default: OpenLiteSpeed, Full installation (postfix, powerdns, ftp), Local MySQL - # These match what the user selected in the interactive prompts install_args+=("--postfix" "ON") install_args+=("--powerdns" "ON") install_args+=("--ftp" "ON") install_args+=("--remotemysql" "OFF") - install_args+=("--mariadb-version" "${MARIADB_VER:-11.8}") + # Only pass --mariadb-version if this install.py supports it (avoids "unrecognized arguments" on older archives) + if grep -q "mariadb-version\|mariadb_version" "$installer_py" 2>/dev/null; then + install_args+=("--mariadb-version" "${MARIADB_VER:-11.8}") + fi if [ "$DEBUG_MODE" = true ]; then # Note: install.py doesn't have --debug, but we can set it via environment @@ -2698,14 +2701,17 @@ parse_arguments() { shift ;; --mariadb-version) - if [ -n "$2" ] && [ "$2" = "11.8" ]; then + if [ -n "$2" ] && [ "$2" = "10.11" ]; then + MARIADB_VER="10.11" + shift 2 + elif [ -n "$2" ] && [ "$2" = "11.8" ]; then MARIADB_VER="11.8" shift 2 elif [ -n "$2" ] && [ "$2" = "12.1" ]; then MARIADB_VER="12.1" shift 2 else - echo "ERROR: --mariadb-version requires 11.8 or 12.1" + echo "ERROR: --mariadb-version requires 10.11, 11.8 or 12.1" exit 1 fi ;; @@ -2718,7 +2724,7 @@ parse_arguments() { echo "Options:" echo " -b, --branch BRANCH Install from specific branch/commit" echo " -v, --version VER Install specific version (auto-adds v prefix)" - echo " --mariadb-version VER MariaDB version: 11.8 or 12.1 (asked first if omitted)" + echo " --mariadb-version VER MariaDB version: 10.11, 11.8 or 12.1 (asked first if omitted)" echo " --debug Enable debug mode" echo " --auto Auto mode without prompts (MariaDB still asked first unless --mariadb-version)" echo " -h, --help Show this help message" @@ -2732,6 +2738,7 @@ parse_arguments() { echo " $0 -v 2.4.3 # Install version 2.4.3" echo " $0 -b main # Install from main branch" echo " $0 -b a1b2c3d4 # Install from specific commit" + echo " $0 --mariadb-version 10.11 # Use MariaDB 10.11 (same as v2.4.4 style)" echo " $0 --mariadb-version 12.1 # Use MariaDB 12.1 (no prompt)" echo " $0 --auto --mariadb-version 11.8 # Fully non-interactive with MariaDB 11.8" echo "" diff --git a/cyberpanel_upgrade.sh b/cyberpanel_upgrade.sh index c3aea01d1..601854724 100644 --- a/cyberpanel_upgrade.sh +++ b/cyberpanel_upgrade.sh @@ -11,6 +11,16 @@ #Please use variable/functions name as MySomething or My_Something, and please try not to use too-short abbreviation :) #Please use On/Off, True/False, Yes/No. +# Require root immediately so all later steps (logs, /root, /usr/local/CyberCP) succeed +if [[ $(id -u) -ne 0 ]] 2>/dev/null; then + echo "" + echo "This script must be run as root." + echo "Run: sudo bash <(curl -sL https://raw.githubusercontent.com/master3395/cyberpanel/stable/cyberpanel_upgrade.sh) -b v2.5.5-dev" + echo "Or: sudo su - then run the same command without sudo" + echo "" + exit 1 +fi + Sudo_Test=$(set) #for SUDO check @@ -95,20 +105,21 @@ echo -e "\n${1}" >> /var/log/upgradeLogs.txt Check_Root() { echo -e "\nChecking root privileges..." + # If we're actually root (uid 0), allow regardless of SUDO in environment (e.g. curl | sudo bash) + if [[ $(id -u) -eq 0 ]] 2>/dev/null; then + echo -e "\nYou are running as root...\n" + return 0 + fi + if echo "$Sudo_Test" | grep SUDO >/dev/null; then echo -e "\nYou are using SUDO, please run as root user...\n" echo -e "\nIf you don't have direct access to root user, please run \e[31msudo su -\e[39m command (do NOT miss the \e[31m-\e[39m at end or it will fail) and then run installation command again." - exit + exit 1 fi - if [[ $(id -u) != 0 ]] >/dev/null; then - echo -e "\nYou must run as root user to install CyberPanel...\n" - echo -e "or run the following command: (do NOT miss the quotes)" - echo -e "\e[31msudo su -c \"sh <(curl https://cyberpanel.sh || wget -O - https://cyberpanel.sh)\"\e[39m" - exit 1 - else - echo -e "\nYou are running as root...\n" - fi + echo -e "\nYou must run as root user to install CyberPanel...\n" + echo -e "Run: \e[31msudo su -\e[39m then run this script again, or: curl -sL | sudo bash -s -- " + exit 1 } Check_Server_IP() { @@ -234,7 +245,13 @@ if [[ "$1" = *.*.* ]]; then echo -e "\nYou must use version number higher than 2.3.4" exit else - Branch_Name="v${1//[[:space:]]/}" + raw="${1//[[:space:]]/}" + # Do not add "v" if user already passed e.g. v2.5.5-dev (avoids vv2.5.5-dev) + if [[ "$raw" = v* ]]; then + Branch_Name="$raw" + else + Branch_Name="v$raw" + fi echo -e "\nSet branch name to $Branch_Name...\n" fi else @@ -349,12 +366,12 @@ if [[ "$*" = *"--no-system-update"* ]]; then Skip_System_Update="yes" echo -e "\nUsing --no-system-update: skipping full system package update.\n" fi -# Parse --mariadb-version 11.8|12.1 (default 11.8) +# Parse --mariadb-version 10.11|11.8|12.1 (default 11.8) if [[ "$*" = *"--mariadb-version "* ]]; then MARIADB_VER=$(echo "$*" | sed -n 's/.*--mariadb-version \([^ ]*\).*/\1/p' | head -1) MARIADB_VER="${MARIADB_VER:-11.8}" fi -if [[ "$MARIADB_VER" != "11.8" ]] && [[ "$MARIADB_VER" != "12.1" ]]; then +if [[ "$MARIADB_VER" != "10.11" ]] && [[ "$MARIADB_VER" != "11.8" ]] && [[ "$MARIADB_VER" != "12.1" ]]; then MARIADB_VER="11.8" fi } @@ -1671,12 +1688,16 @@ if [[ "$*" != *"--branch "* ]] && [[ "$*" != *"-b "* ]] ; then Pre_Upgrade_Branch_Input fi -# Prompt for MariaDB version if not set via --mariadb-version (default 11.8). Downgrade supported (e.g. re-run with --mariadb-version 11.8). +# Prompt for MariaDB version if not set via --mariadb-version (default 11.8). Options: 10.11, 11.8, 12.1. if [[ "$*" != *"--mariadb-version "* ]]; then - echo -e "\nMariaDB version: \e[31m11.8\e[39m LTS (default) or \e[31m12.1\e[39m. You can switch later by re-running with --mariadb-version 11.8 or 12.1." - echo -e "Press Enter for 11.8 LTS, or type \e[31m12.1\e[39m and Enter for 12.1 (5 sec timeout): " + echo -e "\nMariaDB version: \e[31m10.11\e[39m, \e[31m11.8\e[39m LTS (default) or \e[31m12.1\e[39m. You can switch later by re-running with --mariadb-version 10.11, 11.8 or 12.1." + echo -e "Press Enter for 11.8 LTS, or type \e[31m10.11\e[39m or \e[31m12.1\e[39m (5 sec timeout): " read -r -t 5 Tmp_MariaDB_Ver || true - if [[ "$Tmp_MariaDB_Ver" = "12.1" ]]; then + Tmp_MariaDB_Ver="${Tmp_MariaDB_Ver// /}" + if [[ "$Tmp_MariaDB_Ver" = "10.11" ]]; then + MARIADB_VER="10.11" + echo -e "MariaDB 10.11 selected.\n" + elif [[ "$Tmp_MariaDB_Ver" = "12.1" ]]; then MARIADB_VER="12.1" echo -e "MariaDB 12.1 selected.\n" else diff --git a/install/install.py b/install/install.py index 0c29f9c8e..66b6d0280 100644 --- a/install/install.py +++ b/install/install.py @@ -1889,12 +1889,12 @@ module cyberpanel_ols { if is_installed: self.stdOut(f"MariaDB/MySQL is already installed (version: {installed_version}), skipping installation", 1) - # Use existing if already on 11.x or 12.x + # Use existing if already on 10.x, 11.x or 12.x if major_minor and major_minor != "unknown": try: major_ver = float(major_minor) - if major_ver >= 11.0: - self.stdOut("Using existing MariaDB installation (11.x/12.x)", 1) + if major_ver >= 10.0: + self.stdOut("Using existing MariaDB installation (10.x/11.x/12.x)", 1) self.startMariaDB() self.changeMYSQLRootPassword() self.fixMariaDB() @@ -1902,12 +1902,15 @@ module cyberpanel_ols { except (ValueError, TypeError): pass - # Set up MariaDB repository only if not already installed (version from --mariadb-version, default 11.8) + # Set up MariaDB repository only if not already installed (version from --mariadb-version: 10.11, 11.8 or 12.1) mariadb_ver = getattr(preFlightsChecks, 'mariadb_version', '11.8') command = f'curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | bash -s -- --mariadb-server-version={mariadb_ver}' self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - - command = 'dnf install mariadb-server mariadb-devel mariadb-client-utils -y' + # Use --nobest for 10.11 and 11.8 on el9 to avoid MariaDB-client dependency resolution issues + if mariadb_ver in ('10.11', '11.8'): + command = 'dnf install -y --nobest mariadb-server mariadb-devel mariadb-client-utils' + else: + command = 'dnf install mariadb-server mariadb-devel mariadb-client-utils -y' self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) # Verify MariaDB was installed successfully before proceeding @@ -6485,12 +6488,12 @@ def main(): parser.add_argument('--mysqluser', help='MySQL user if remote is chosen.') parser.add_argument('--mysqlpassword', help='MySQL password if remote is chosen.') parser.add_argument('--mysqlport', help='MySQL port if remote is chosen.') - parser.add_argument('--mariadb-version', default='11.8', help='MariaDB version: 11.8 (LTS, default) or 12.1') + parser.add_argument('--mariadb-version', default='11.8', help='MariaDB version: 10.11, 11.8 (LTS, default) or 12.1') args = parser.parse_args() # Normalize and validate MariaDB version choice (default 11.8) mariadb_ver = (getattr(args, 'mariadb_version', None) or '11.8').strip() - if mariadb_ver not in ('11.8', '12.1'): + if mariadb_ver not in ('10.11', '11.8', '12.1'): mariadb_ver = '11.8' preFlightsChecks.mariadb_version = mariadb_ver From c0b1044f455beb79269a989b8739aa6eca97c2a4 Mon Sep 17 00:00:00 2001 From: master3395 Date: Wed, 4 Feb 2026 21:33:26 +0100 Subject: [PATCH 03/16] Install fixes: venv creation, composer.sh download, SSH key non-interactive, ensure 8090/7080 accessible - install.py: create venv at /usr/local/CyberCP if missing; add system python3 fallback for migrations; download composer.sh before chmod; remove existing SSH keys before ssh-keygen - installCyberPanel.py: remove MariaDB-server from dnf exclude before install - cyberpanel.sh: ensure ports 8090 and 7080 listening after install; final status shows port accessibility - backupUtilities.py: remove existing cyberpanel keys before ssh-keygen to avoid Overwrite prompt --- cyberpanel.sh | 132 +++++++++++++++++++---------------- install/install.py | 124 ++++++++++++++++++++++++-------- install/installCyberPanel.py | 19 +++++ plogical/backupUtilities.py | 7 ++ 4 files changed, 191 insertions(+), 91 deletions(-) diff --git a/cyberpanel.sh b/cyberpanel.sh index 4adde917a..aff29677c 100644 --- a/cyberpanel.sh +++ b/cyberpanel.sh @@ -968,67 +968,9 @@ except Exception as e: if [ -f "$installer_py" ]; then print_status "Using install/install.py directly for installation (non-interactive mode)" - # CRITICAL: Patch install.py to exclude MariaDB-server from dnf/yum commands - if [ -n "$MARIADB_VERSION" ] && [ "$major_ver" -lt 12 ] 2>/dev/null; then - print_status "Patching install.py to exclude MariaDB-server from installation commands..." - - # Create backup - cp "$installer_py" "${installer_py}.backup" 2>/dev/null || true - - # Patch install.py to add --exclude=MariaDB-server* to dnf/yum install commands - python3 -c " -import re -import sys - -try: - with open('$installer_py', 'r') as f: - content = f.read() - - original_content = content - - # Pattern: Add --exclude=MariaDB-server* to dnf/yum install commands that install mariadb-server - def add_exclude(match): - cmd = match.group(0) - # Check if --exclude is already present - if '--exclude=MariaDB-server' in cmd: - return cmd - # Add --exclude=MariaDB-server* after install and flags, before packages - return re.sub(r'((?:dnf|yum)\s+install\s+(?:-[^\s]+\s+)*)', r'\1--exclude=MariaDB-server* ', cmd, flags=re.IGNORECASE) - - # Find all dnf/yum install commands that mention mariadb-server - content = re.sub( - r'(?:dnf|yum)\s+install[^;]*?mariadb-server[^;]*', - add_exclude, - content, - flags=re.IGNORECASE | re.MULTILINE - ) - - # Also handle MariaDB-server (capitalized) and in Python strings - content = re.sub( - r'(\"|\')(?:dnf|yum)\s+install[^\"]*?mariadb-server[^\"]*(\"|\')', - lambda m: m.group(1) + re.sub(r'((?:dnf|yum)\s+install\s+(?:-[^\s]+\s+)*)', r'\1--exclude=MariaDB-server* ', m.group(0)[1:-1], flags=re.IGNORECASE) + m.group(2), - content, - flags=re.IGNORECASE | re.MULTILINE - ) - - # Only write if content changed - if content != original_content: - with open('$installer_py', 'w') as f: - f.write(content) - print('install.py patched successfully') - else: - print('No changes needed in install.py') - -except Exception as e: - print(f'Error patching install.py: {e}') - sys.exit(1) -" 2>/dev/null && print_status "install.py patched successfully" || { - # Fallback: Simple sed-based patching if Python fails - sed -i 's/\(dnf\|yum\) install\([^;]*\)mariadb-server/\1 install\2--exclude=MariaDB-server* mariadb-server/gi' "$installer_py" 2>/dev/null - sed -i 's/\(dnf\|yum\) install\([^;]*\)MariaDB-server/\1 install\2--exclude=MariaDB-server* MariaDB-server/gi' "$installer_py" 2>/dev/null - print_status "install.py patched (fallback method)" - } - fi + # NOTE: We do NOT patch install.py to add --exclude=MariaDB-server* to dnf install. + # That would block the initial MariaDB-server install. install.py now clears dnf exclude + # before installing MariaDB and uses official MariaDB-server packages. # If MariaDB 10.x is installed, disable repositories right before running installer if [ -n "$MARIADB_VERSION" ] && [ -f /tmp/cyberpanel_repo_monitor.pid ]; then @@ -1421,11 +1363,61 @@ EOF # Give services a moment to start sleep 3 + # Ensure both 8090 (CyberPanel) and 7080 (LiteSpeed/OLS) are accessible + echo " • Ensuring ports 8090 and 7080 are accessible..." + port_check() { + local port=$1 + command -v ss >/dev/null 2>&1 && ss -tlnp 2>/dev/null | grep -q ":$port " && return 0 + command -v netstat >/dev/null 2>&1 && netstat -tlnp 2>/dev/null | grep -q ":$port " && return 0 + return 1 + } + max_attempts=18 + attempt=0 + while [ $attempt -lt $max_attempts ]; do + need_restart=false + systemctl is-active --quiet mariadb || { systemctl start mariadb 2>/dev/null; need_restart=true; } + systemctl is-active --quiet lsws 2>/dev/null || { [ -x /usr/local/lsws/bin/lswsctrl ] && systemctl start lsws 2>/dev/null; need_restart=true; } + systemctl is-active --quiet lscpd 2>/dev/null || { systemctl start lscpd 2>/dev/null; need_restart=true; } + [ "$need_restart" = true ] && sleep 5 + if port_check 8090 && port_check 7080; then + echo " ✓ Port 8090 (CyberPanel) and 7080 (OpenLiteSpeed) are listening" + break + fi + attempt=$((attempt + 1)) + [ $attempt -lt $max_attempts ] && sleep 5 + done + if ! port_check 8090 || ! port_check 7080; then + systemctl start lscpd 2>/dev/null + systemctl start lsws 2>/dev/null + sleep 10 + if port_check 8090 && port_check 7080; then + echo " ✓ Port 8090 and 7080 are now listening" + else + echo " ⚠ One or both ports not yet listening. Run: systemctl start mariadb lsws lscpd" + fi + fi + echo " ✓ Post-installation configurations completed" } +# Helper: check if a port is listening +_port_listening() { + local port=$1 + command -v ss >/dev/null 2>&1 && ss -tlnp 2>/dev/null | grep -q ":$port " && return 0 + command -v netstat >/dev/null 2>&1 && netstat -tlnp 2>/dev/null | grep -q ":$port " && return 0 + return 1 +} + # Function to show status summary show_status_summary() { + # Last-chance: try to start services so 8090 and 7080 are accessible + if ! _port_listening 8090 || ! _port_listening 7080; then + systemctl start mariadb 2>/dev/null || true + systemctl start lsws 2>/dev/null || true + systemctl start lscpd 2>/dev/null || true + sleep 8 + fi + echo "===============================================================================================================" echo " FINAL STATUS CHECK" echo "===============================================================================================================" @@ -1453,6 +1445,22 @@ show_status_summary() { echo " ✓ CyberPanel Application - Running" else echo " ✗ CyberPanel Application - Not Running (may take a moment to start)" + all_services_running=false + fi + + echo "" + echo "Port Accessibility:" + if _port_listening 8090; then + echo " ✓ Port 8090 (CyberPanel) - Accessible" + else + echo " ✗ Port 8090 (CyberPanel) - Not listening (run: systemctl start lscpd)" + all_services_running=false + fi + if _port_listening 7080; then + echo " ✓ Port 7080 (OpenLiteSpeed) - Accessible" + else + echo " ✗ Port 7080 (OpenLiteSpeed) - Not listening (run: systemctl start lsws)" + all_services_running=false fi # Get the actual password that was set @@ -1481,7 +1489,7 @@ show_status_summary() { echo "===============================================================================================================" if [ "$all_services_running" = true ]; then - echo "✓ Installation completed successfully!" + echo "✓ Installation completed successfully! Ports 8090 and 7080 are accessible." else echo "⚠ Installation completed with warnings. Some services may need attention." fi diff --git a/install/install.py b/install/install.py index 66b6d0280..eecb7e113 100644 --- a/install/install.py +++ b/install/install.py @@ -1906,11 +1906,34 @@ module cyberpanel_ols { mariadb_ver = getattr(preFlightsChecks, 'mariadb_version', '11.8') command = f'curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | bash -s -- --mariadb-server-version={mariadb_ver}' self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - # Use --nobest for 10.11 and 11.8 on el9 to avoid MariaDB-client dependency resolution issues + # Allow MariaDB-server to be installed: remove from dnf exclude if present (e.g. from previous run or cyberpanel.sh) + dnf_conf = '/etc/dnf/dnf.conf' + if os.path.exists(dnf_conf): + try: + with open(dnf_conf, 'r') as f: + dnf_content = f.read() + if 'MariaDB-server' in (dnf_content or '') and 'exclude=' in (dnf_content or ''): + # Remove MariaDB-server and MariaDB-server* from exclude= line(s) + def strip_mariadb_exclude(match): + line = match.group(0) + rest = re.sub(r'\bMariaDB-server\*?\s*', '', line).strip() + if rest == 'exclude=' or rest == 'exclude': + return '' + return rest.rstrip() + '\n' + new_content = re.sub(r'exclude=[^\n]*', strip_mariadb_exclude, dnf_content) + new_content = re.sub(r'\n\n+', '\n', new_content) + if new_content != dnf_content: + with open(dnf_conf, 'w') as f: + f.write(new_content) + self.stdOut("Temporarily removed MariaDB-server from dnf exclude for installation", 1) + except Exception as e: + self.stdOut(f"Warning: Could not adjust dnf exclude: {e}", 1) + # Install from official MariaDB repo (capitalized package names); --nobest for 10.11/11.8 on el9 + mariadb_packages = 'MariaDB-server MariaDB-client MariaDB-backup MariaDB-devel' if mariadb_ver in ('10.11', '11.8'): - command = 'dnf install -y --nobest mariadb-server mariadb-devel mariadb-client-utils' + command = f'dnf install -y --nobest {mariadb_packages}' else: - command = 'dnf install mariadb-server mariadb-devel mariadb-client-utils -y' + command = f'dnf install -y {mariadb_packages}' self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) # Verify MariaDB was installed successfully before proceeding @@ -3267,7 +3290,36 @@ password="%s" logging.InstallLog.writeToFile("settings.py updated!") - # self.setupVirtualEnv(self.distro) + # Create Python venv at /usr/local/CyberCP if missing (install.py run from temp dir does not run venvsetup.sh) + if not os.path.exists("/usr/local/CyberCP/bin/python"): + logging.InstallLog.writeToFile("Creating Python virtual environment at /usr/local/CyberCP...") + preFlightsChecks.stdOut("Creating Python virtual environment...") + try: + r = subprocess.run( + [sys.executable or "python3", "-m", "venv", "/usr/local/CyberCP"], + timeout=120, capture_output=True, text=True, cwd="/usr/local/CyberCP" + ) + if r.returncode != 0: + logging.InstallLog.writeToFile("venv create stderr: " + (r.stderr or "")[:500]) + if r.returncode == 0 and os.path.exists("/usr/local/CyberCP/bin/pip"): + req_file = "/usr/local/CyberCP/requirments.txt" + if not os.path.exists(req_file): + req_file = "/usr/local/CyberCP/requirements.txt" + if os.path.exists(req_file): + subprocess.run( + ["/usr/local/CyberCP/bin/pip", "install", "-r", req_file, "--quiet"], + timeout=600, cwd="/usr/local/CyberCP", capture_output=True + ) + else: + subprocess.run( + ["/usr/local/CyberCP/bin/pip", "install", "Django", "PyMySQL", "requests", "cryptography", "psutil", "--quiet"], + timeout=180, cwd="/usr/local/CyberCP", capture_output=True + ) + if os.path.exists("/usr/local/CyberCP/bin/python"): + logging.InstallLog.writeToFile("Virtual environment created successfully") + preFlightsChecks.stdOut("Virtual environment created", 1) + except Exception as e: + logging.InstallLog.writeToFile("Venv create warning: " + str(e)) # Now run Django migrations since we're in /usr/local/CyberCP and database exists os.chdir("/usr/local/CyberCP") @@ -3303,46 +3355,53 @@ password="%s" logging.InstallLog.writeToFile("Migration cleanup completed") - # Ensure virtual environment is properly set up - logging.InstallLog.writeToFile("Ensuring virtual environment is properly set up...") + # Ensure virtual environment or system Python is available + logging.InstallLog.writeToFile("Ensuring Python is available for migrations...") if not self.ensureVirtualEnvironmentSetup(): - logging.InstallLog.writeToFile("ERROR: Virtual environment setup failed!", 0) - preFlightsChecks.stdOut("ERROR: Virtual environment setup failed!", 0) - return False + logging.InstallLog.writeToFile("WARNING: No venv found; will try system Python", 1) - # Find the correct Python virtual environment path (prefer CyberCP - app install path) + # Find Python: prefer venv, then system python3 (avoids FileNotFoundError for /usr/local/CyberPanel/bin/python) python_paths = [ "/usr/local/CyberCP/bin/python", "/usr/local/CyberPanel/bin/python", - "/usr/local/CyberPanel-venv/bin/python" + "/usr/local/CyberPanel-venv/bin/python", + "/usr/bin/python3", + "/usr/local/bin/python3", ] - + if sys.executable and sys.executable not in python_paths: + python_paths.append(sys.executable) + python_path = None for path in python_paths: - if os.path.exists(path): - python_path = path - logging.InstallLog.writeToFile(f"Found Python virtual environment at: {path}") - break - + if path and os.path.exists(path): + try: + r = subprocess.run([path, "--version"], capture_output=True, text=True, timeout=5) + if r.returncode == 0: + python_path = path + logging.InstallLog.writeToFile(f"Using Python at: {path}") + break + except Exception: + continue + if not python_path: - logging.InstallLog.writeToFile("ERROR: No Python virtual environment found!", 0) - preFlightsChecks.stdOut("ERROR: No Python virtual environment found!", 0) + logging.InstallLog.writeToFile("ERROR: No working Python found for migrations!", 0) + preFlightsChecks.stdOut("ERROR: No working Python found!", 0) return False # Create migrations in dependency order - loginSystem first since other apps depend on it logging.InstallLog.writeToFile("Creating migrations for loginSystem first...") command = f"{python_path} manage.py makemigrations loginSystem --noinput" - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) # Now create migrations for all other apps logging.InstallLog.writeToFile("Creating migrations for all other apps...") command = f"{python_path} manage.py makemigrations --noinput" - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) # Apply all migrations logging.InstallLog.writeToFile("Applying all migrations...") command = f"{python_path} manage.py migrate --noinput" - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) logging.InstallLog.writeToFile("Django migrations completed successfully!") preFlightsChecks.stdOut("Django migrations completed successfully!") @@ -3354,7 +3413,7 @@ password="%s" self.downloadCDNLibraries() command = f"{python_path} manage.py collectstatic --noinput --clear" - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) ## Moving static content to lscpd location command = 'mv static /usr/local/CyberCP/public/' @@ -5308,10 +5367,10 @@ user_query = SELECT email as user, password, 'vmail' as uid, 'vmail' as gid, '/h try: os.remove(f) except OSError: - pass + subprocess.run(["rm", "-f", f], timeout=5, capture_output=True) command = "ssh-keygen -f /root/.ssh/cyberpanel -t rsa -N ''" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR, True) except BaseException as msg: logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [install_default_keys]") @@ -5701,11 +5760,18 @@ milter_default_action = accept os.chdir(self.cwd) - command = "chmod +x composer.sh" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + # Download composer.sh if missing (e.g. when run from temp dir without repo file) + composer_sh = os.path.join(self.cwd, "composer.sh") + if not os.path.exists(composer_sh) or not os.path.isfile(composer_sh): + command = "wget -q https://cyberpanel.sh/composer.sh -O " + composer_sh + preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - command = "./composer.sh" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + if os.path.exists(composer_sh): + command = "chmod +x " + composer_sh + preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + + command = "bash " + os.path.abspath(composer_sh) + preFlightsChecks.call(command, self.distro, "./composer.sh", command, 1, 0, os.EX_OSERR, True) except OSError as msg: logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [setupPHPAndComposer]") diff --git a/install/installCyberPanel.py b/install/installCyberPanel.py index 1e0aed194..7861be4ee 100644 --- a/install/installCyberPanel.py +++ b/install/installCyberPanel.py @@ -922,6 +922,25 @@ gpgcheck=1 command = 'dnf clean all' install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + # Allow MariaDB-server to be installed: remove from dnf exclude if present + dnf_conf = '/etc/dnf/dnf.conf' + if os.path.exists(dnf_conf): + try: + with open(dnf_conf, 'r') as f: + dnf_content = f.read() + if 'MariaDB-server' in dnf_content and 'exclude=' in dnf_content: + new_content = re.sub( + r'(exclude=[^\n]*)', + lambda m: re.sub(r'\bMariaDB-server\*?\s*', '', m.group(1)).strip(), + dnf_content + ) + if new_content != dnf_content: + with open(dnf_conf, 'w') as f: + f.write(new_content) + install_utils.writeToFile("Removed MariaDB-server from dnf exclude for installation") + except Exception: + pass + # Use --nobest so server+client resolve from same repo (avoids AlmaLinux 9 dependency conflict) command = 'dnf install -y --nobest MariaDB-server MariaDB-client MariaDB-backup' diff --git a/plogical/backupUtilities.py b/plogical/backupUtilities.py index 2f1e11bb1..bfb0eb5a9 100644 --- a/plogical/backupUtilities.py +++ b/plogical/backupUtilities.py @@ -1412,6 +1412,13 @@ class backupUtilities: if os.path.exists('/root/.ssh/cyberpanel.pub'): pass else: + # Remove existing key files so ssh-keygen never prompts "Overwrite (y/n)?" + for f in ('/root/.ssh/cyberpanel', '/root/.ssh/cyberpanel.pub'): + if os.path.exists(f): + try: + os.remove(f) + except OSError: + pass command = "ssh-keygen -f /root/.ssh/cyberpanel -t rsa -N ''" ProcessUtilities.executioner(command, 'root', True) From 1a83f4df38c610a64a8faf11482d48c4a63fb034 Mon Sep 17 00:00:00 2001 From: master3395 Date: Wed, 4 Feb 2026 21:42:40 +0100 Subject: [PATCH 04/16] Clear MariaDB-server from dnf exclude before install; dev branch archive URL - cyberpanel.sh: clear MariaDB-server* from dnf.conf/yum.conf before running installer so MariaDB-server can be installed (fixes exclude filtering on retry/partial install) - cyberpanel.sh: try refs/heads/v2.5.5-dev.tar.gz for branch archive; remove installer MariaDB patch; softer chmod message - install.py: sed fallback to strip MariaDB-server from dnf exclude if Python logic fails --- cyberpanel.sh | 108 +++++++++++++-------------------------------- install/install.py | 9 +++- 2 files changed, 39 insertions(+), 78 deletions(-) diff --git a/cyberpanel.sh b/cyberpanel.sh index aff29677c..54fd8aba4 100644 --- a/cyberpanel.sh +++ b/cyberpanel.sh @@ -789,19 +789,23 @@ except: echo "Downloading from: https://raw.githubusercontent.com/master3395/cyberpanel/v2.5.5-dev/cyberpanel.sh" # First, try to download the repository archive to get the correct installer - local archive_url="https://github.com/master3395/cyberpanel/archive/v2.5.5-dev.tar.gz" + # GitHub: branch archives use refs/heads/BRANCH; tag archives use refs/tags/TAG or /TAG + local archive_url="" local installer_url="https://raw.githubusercontent.com/master3395/cyberpanel/v2.5.5-dev/cyberpanel.sh" - - # Test if the development branch archive exists - if curl -s --head "$archive_url" | grep -q "200 OK"; then + if curl -s --head "https://github.com/master3395/cyberpanel/archive/refs/heads/v2.5.5-dev.tar.gz" | grep -q "200 OK"; then + archive_url="https://github.com/master3395/cyberpanel/archive/refs/heads/v2.5.5-dev.tar.gz" + echo " Using development branch (v2.5.5-dev) from master3395/cyberpanel" + elif curl -s --head "https://github.com/master3395/cyberpanel/archive/v2.5.5-dev.tar.gz" | grep -q "200 OK"; then + archive_url="https://github.com/master3395/cyberpanel/archive/v2.5.5-dev.tar.gz" echo " Using development branch (v2.5.5-dev) from master3395/cyberpanel" else echo " Development branch archive not available, trying installer script directly..." - # Test if the installer script exists if ! curl -s --head "$installer_url" | grep -q "200 OK"; then echo " Development branch not available, falling back to stable" installer_url="https://raw.githubusercontent.com/master3395/cyberpanel/stable/cyberpanel.sh" archive_url="https://github.com/master3395/cyberpanel/archive/stable.tar.gz" + else + archive_url="https://github.com/master3395/cyberpanel/archive/refs/heads/v2.5.5-dev.tar.gz" fi fi @@ -811,81 +815,18 @@ except: return 1 fi - # CRITICAL: Patch the installer script to skip MariaDB installation if 10.x is already installed - if [ -n "$MARIADB_VERSION" ] && [ "$major_ver" -lt 12 ] 2>/dev/null; then - print_status "Patching installer script to skip MariaDB installation..." - - # Create a backup - cp cyberpanel_installer.sh cyberpanel_installer.sh.backup - - # Use Python to properly patch the installer script - python3 -c " -import re -import sys - -try: - with open('cyberpanel_installer.sh', 'r') as f: - content = f.read() + # Do NOT patch installer to add --exclude=MariaDB-server*: it blocks initial MariaDB install + # and causes "MariaDB-server requires MariaDB-client but none of the providers can be installed". - original_content = content - - # Pattern: Add --exclude=MariaDB-server* to dnf/yum install commands that install mariadb-server - # Match: (dnf|yum) install [flags] [packages including mariadb-server] - def add_exclude(match): - cmd = match.group(0) - # Check if --exclude is already present - if '--exclude=MariaDB-server' in cmd: - return cmd - # Add --exclude=MariaDB-server* after install and flags, before packages - return re.sub(r'((?:dnf|yum)\s+install\s+(?:-[^\s]+\s+)*)', r'\1--exclude=MariaDB-server* ', cmd, flags=re.IGNORECASE) - - # Find all dnf/yum install commands that mention mariadb-server - content = re.sub( - r'(?:dnf|yum)\s+install[^;]*?mariadb-server[^;]*', - add_exclude, - content, - flags=re.IGNORECASE | re.MULTILINE - ) - - # Also handle MariaDB-server (capitalized) - content = re.sub( - r'(?:dnf|yum)\s+install[^;]*?MariaDB-server[^;]*', - add_exclude, - content, - flags=re.IGNORECASE | re.MULTILINE - ) - - # Only write if content changed - if content != original_content: - with open('cyberpanel_installer.sh', 'w') as f: - f.write(content) - print('Installer script patched successfully') - else: - print('No changes needed in installer script') - -except Exception as e: - print(f'Error patching installer script: {e}') - sys.exit(1) -" 2>/dev/null && print_status "Installer script patched successfully" || { - # Fallback: Simple sed-based patching if Python fails - sed -i 's/\(dnf\|yum\) install\([^;]*\)mariadb-server/\1 install\2--exclude=MariaDB-server* mariadb-server/gi' cyberpanel_installer.sh 2>/dev/null - sed -i 's/\(dnf\|yum\) install\([^;]*\)MariaDB-server/\1 install\2--exclude=MariaDB-server* MariaDB-server/gi' cyberpanel_installer.sh 2>/dev/null - print_status "Installer script patched (fallback method)" - } - - print_status "Installer script patched to exclude MariaDB-server from installation" - fi - - # Make script executable and verify - chmod 755 cyberpanel_installer.sh 2>/dev/null || true + # Make script executable (use full path in case cwd has noexec) + chmod 755 cyberpanel_installer.sh 2>/dev/null || chmod +x cyberpanel_installer.sh 2>/dev/null || true if [ ! -x "cyberpanel_installer.sh" ]; then - print_status "WARNING: Could not make cyberpanel_installer.sh executable, will use bash to execute" + print_status "Note: Script will be run with bash (executable bit not set)" fi - # Download the install directory + # Download the install directory (use archive_url set above; may be branch or stable) echo "Downloading installation files..." - local archive_url="https://github.com/master3395/cyberpanel/archive/v2.5.5-dev.tar.gz" - if [ "$installer_url" = "https://raw.githubusercontent.com/master3395/cyberpanel/stable/cyberpanel.sh" ]; then + if [ -z "$archive_url" ] || [ "$installer_url" = "https://raw.githubusercontent.com/master3395/cyberpanel/stable/cyberpanel.sh" ]; then archive_url="https://github.com/master3395/cyberpanel/archive/stable.tar.gz" fi @@ -972,6 +913,19 @@ except Exception as e: # That would block the initial MariaDB-server install. install.py now clears dnf exclude # before installing MariaDB and uses official MariaDB-server packages. + # Clear MariaDB-server from dnf/yum exclude so the installer can install or reinstall it + # (cyberpanel.sh may have added it earlier when 10.x was detected; partial installs leave exclude in place) + for conf in /etc/dnf/dnf.conf /etc/yum.conf; do + if [ -f "$conf" ] && grep -q "exclude=.*MariaDB-server" "$conf" 2>/dev/null; then + sed -i '/^exclude=/s/MariaDB-server\*\s*//g' "$conf" + sed -i '/^exclude=/s/\s*MariaDB-server\*//g' "$conf" + sed -i '/^exclude=/s/MariaDB-server\s*//g' "$conf" + sed -i '/^exclude=\s*$/d' "$conf" + sed -i '/^exclude=$/d' "$conf" + print_status "Cleared MariaDB-server from exclude in $conf for installation" + fi + done + # If MariaDB 10.x is installed, disable repositories right before running installer if [ -n "$MARIADB_VERSION" ] && [ -f /tmp/cyberpanel_repo_monitor.pid ]; then # Call the disable function one more time before installer runs @@ -1698,7 +1652,7 @@ show_version_selection() { echo "" echo " 1. Latest Stable (Recommended)" echo " 2. v2.5.5-dev (Development)" - echo " 3. v2.5.4 (Previous Stable)" + echo " 3. v2.4.4 (Previous Stable)" echo " 4. Custom Branch Name" echo " 5. Custom Commit Hash" echo "" @@ -1719,7 +1673,7 @@ show_version_selection() { break ;; 3) - BRANCH_NAME="v2.5.4" + BRANCH_NAME="v2.4.4" break ;; 4) diff --git a/install/install.py b/install/install.py index eecb7e113..9bdae4b48 100644 --- a/install/install.py +++ b/install/install.py @@ -1908,7 +1908,7 @@ module cyberpanel_ols { self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) # Allow MariaDB-server to be installed: remove from dnf exclude if present (e.g. from previous run or cyberpanel.sh) dnf_conf = '/etc/dnf/dnf.conf' - if os.path.exists(dnf_conf): + if os.path.exists(dnf_conf) and ('MariaDB-server' in open(dnf_conf).read()): try: with open(dnf_conf, 'r') as f: dnf_content = f.read() @@ -1928,6 +1928,13 @@ module cyberpanel_ols { self.stdOut("Temporarily removed MariaDB-server from dnf exclude for installation", 1) except Exception as e: self.stdOut(f"Warning: Could not adjust dnf exclude: {e}", 1) + # Fallback: use sed so exclude is cleared even if Python path failed + if 'MariaDB-server' in open(dnf_conf).read(): + subprocess.run( + "sed -i '/^exclude=/s/MariaDB-server\\*\\s*//g; /^exclude=/s/\\s*MariaDB-server\\*//g; /^exclude=\\s*$/d' " + dnf_conf, + shell=True, timeout=5, capture_output=True + ) + self.stdOut("Temporarily removed MariaDB-server from dnf exclude for installation (fallback)", 1) # Install from official MariaDB repo (capitalized package names); --nobest for 10.11/11.8 on el9 mariadb_packages = 'MariaDB-server MariaDB-client MariaDB-backup MariaDB-devel' if mariadb_ver in ('10.11', '11.8'): From db484ce0d0132427db34dac091eee31e85b45d39 Mon Sep 17 00:00:00 2001 From: master3395 Date: Wed, 4 Feb 2026 21:47:38 +0100 Subject: [PATCH 05/16] Fix makemigrations FileNotFoundError: skip broken venv symlinks when choosing Python - Resolve symlinks and require executable file before using a path - Skip paths that are broken symlinks or not executable (e.g. /usr/local/CyberPanel -> CyberCP when venv missing) - Catch FileNotFoundError/OSError from subprocess when testing --version - Ensures system python3 is used when venv paths are invalid --- install/install.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/install/install.py b/install/install.py index 9bdae4b48..7d6ab6a03 100644 --- a/install/install.py +++ b/install/install.py @@ -3367,7 +3367,8 @@ password="%s" if not self.ensureVirtualEnvironmentSetup(): logging.InstallLog.writeToFile("WARNING: No venv found; will try system Python", 1) - # Find Python: prefer venv, then system python3 (avoids FileNotFoundError for /usr/local/CyberPanel/bin/python) + # Find Python: prefer venv, then system python3 (avoid FileNotFoundError for broken /usr/local/CyberPanel/bin/python) + # Check resolved path so broken symlinks are skipped python_paths = [ "/usr/local/CyberCP/bin/python", "/usr/local/CyberPanel/bin/python", @@ -3380,15 +3381,26 @@ password="%s" python_path = None for path in python_paths: - if path and os.path.exists(path): - try: - r = subprocess.run([path, "--version"], capture_output=True, text=True, timeout=5) - if r.returncode == 0: - python_path = path - logging.InstallLog.writeToFile(f"Using Python at: {path}") - break - except Exception: + if not path: + continue + try: + # Skip broken symlinks: resolve and require executable file + if os.path.lexists(path): + resolved = os.path.realpath(path) + if not os.path.isfile(resolved) or not os.access(resolved, os.X_OK): + continue + elif not os.path.isfile(path) or not os.access(path, os.X_OK): continue + except OSError: + continue + try: + r = subprocess.run([path, "--version"], capture_output=True, text=True, timeout=5) + if r.returncode == 0: + python_path = path + logging.InstallLog.writeToFile(f"Using Python at: {path}") + break + except (FileNotFoundError, OSError, subprocess.SubprocessError): + continue if not python_path: logging.InstallLog.writeToFile("ERROR: No working Python found for migrations!", 0) From 97d12d60f677393b652b24c7f279767e23689df8 Mon Sep 17 00:00:00 2001 From: master3395 Date: Wed, 4 Feb 2026 21:54:23 +0100 Subject: [PATCH 06/16] Fix interactive menu when script is piped (curl | bash): redirect stdin from /dev/tty so Force Reinstall and other prompts read from terminal --- cyberpanel.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cyberpanel.sh b/cyberpanel.sh index 54fd8aba4..5c19974fe 100644 --- a/cyberpanel.sh +++ b/cyberpanel.sh @@ -2839,7 +2839,10 @@ main() { print_status "SUCCESS: Installation completed successfully!" else - # Run interactive mode + # Run interactive mode - ensure stdin is the terminal for prompts (e.g. when script was piped from curl) + if [ ! -t 0 ]; then + exec 0 Date: Wed, 4 Feb 2026 22:05:16 +0100 Subject: [PATCH 07/16] Fix dev branch detection: use curl -L so GitHub archive 302 redirect is followed and v2.5.5-dev is recognized as available --- cyberpanel.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cyberpanel.sh b/cyberpanel.sh index 5c19974fe..4607b698f 100644 --- a/cyberpanel.sh +++ b/cyberpanel.sh @@ -789,18 +789,18 @@ except: echo "Downloading from: https://raw.githubusercontent.com/master3395/cyberpanel/v2.5.5-dev/cyberpanel.sh" # First, try to download the repository archive to get the correct installer - # GitHub: branch archives use refs/heads/BRANCH; tag archives use refs/tags/TAG or /TAG + # GitHub: branch archives use refs/heads/BRANCH; GitHub returns 302 redirect to codeload, so we must use -L local archive_url="" local installer_url="https://raw.githubusercontent.com/master3395/cyberpanel/v2.5.5-dev/cyberpanel.sh" - if curl -s --head "https://github.com/master3395/cyberpanel/archive/refs/heads/v2.5.5-dev.tar.gz" | grep -q "200 OK"; then + if curl -s -L --head "https://github.com/master3395/cyberpanel/archive/refs/heads/v2.5.5-dev.tar.gz" | grep -q "200 OK"; then archive_url="https://github.com/master3395/cyberpanel/archive/refs/heads/v2.5.5-dev.tar.gz" echo " Using development branch (v2.5.5-dev) from master3395/cyberpanel" - elif curl -s --head "https://github.com/master3395/cyberpanel/archive/v2.5.5-dev.tar.gz" | grep -q "200 OK"; then + elif curl -s -L --head "https://github.com/master3395/cyberpanel/archive/v2.5.5-dev.tar.gz" | grep -q "200 OK"; then archive_url="https://github.com/master3395/cyberpanel/archive/v2.5.5-dev.tar.gz" echo " Using development branch (v2.5.5-dev) from master3395/cyberpanel" else echo " Development branch archive not available, trying installer script directly..." - if ! curl -s --head "$installer_url" | grep -q "200 OK"; then + if ! curl -s -L --head "$installer_url" | grep -q "200 OK"; then echo " Development branch not available, falling back to stable" installer_url="https://raw.githubusercontent.com/master3395/cyberpanel/stable/cyberpanel.sh" archive_url="https://github.com/master3395/cyberpanel/archive/stable.tar.gz" From fa320c37f4062fb992a21a37676e87d88dbfa1b3 Mon Sep 17 00:00:00 2001 From: master3395 Date: Wed, 4 Feb 2026 22:13:06 +0100 Subject: [PATCH 08/16] Install: non-interactive SSH keygen + robust Python for migrations - install_default_keys: use shell rm -f and ssh-keygen with stdin=y for no prompt - Migrations: try system Python (/usr/bin/python3) first, then venv paths - Use absolute path /usr/local/CyberCP/manage.py for makemigrations/migrate/collectstatic - Avoid FileNotFoundError for missing /usr/local/CyberPanel/bin/python on fresh install --- install/install.py | 55 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/install/install.py b/install/install.py index 7d6ab6a03..bfbeb45cd 100644 --- a/install/install.py +++ b/install/install.py @@ -3367,17 +3367,19 @@ password="%s" if not self.ensureVirtualEnvironmentSetup(): logging.InstallLog.writeToFile("WARNING: No venv found; will try system Python", 1) - # Find Python: prefer venv, then system python3 (avoid FileNotFoundError for broken /usr/local/CyberPanel/bin/python) - # Check resolved path so broken symlinks are skipped + # Find Python: try system Python first so we never use missing /usr/local/CyberPanel/bin/python + # (venv may not exist yet; /usr/local/CyberPanel is legacy and often missing on fresh install) python_paths = [ - "/usr/local/CyberCP/bin/python", - "/usr/local/CyberPanel/bin/python", - "/usr/local/CyberPanel-venv/bin/python", "/usr/bin/python3", "/usr/local/bin/python3", ] if sys.executable and sys.executable not in python_paths: python_paths.append(sys.executable) + python_paths.extend([ + "/usr/local/CyberCP/bin/python", + "/usr/local/CyberPanel/bin/python", + "/usr/local/CyberPanel-venv/bin/python", + ]) python_path = None for path in python_paths: @@ -3407,19 +3409,25 @@ password="%s" preFlightsChecks.stdOut("ERROR: No working Python found!", 0) return False + manage_py = "/usr/local/CyberCP/manage.py" + if not os.path.isfile(manage_py): + logging.InstallLog.writeToFile("ERROR: %s not found" % manage_py, 0) + preFlightsChecks.stdOut("ERROR: manage.py not found at %s" % manage_py, 0) + return False + # Create migrations in dependency order - loginSystem first since other apps depend on it logging.InstallLog.writeToFile("Creating migrations for loginSystem first...") - command = f"{python_path} manage.py makemigrations loginSystem --noinput" + command = f"{python_path} {manage_py} makemigrations loginSystem --noinput" preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) # Now create migrations for all other apps logging.InstallLog.writeToFile("Creating migrations for all other apps...") - command = f"{python_path} manage.py makemigrations --noinput" + command = f"{python_path} {manage_py} makemigrations --noinput" preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) # Apply all migrations logging.InstallLog.writeToFile("Applying all migrations...") - command = f"{python_path} manage.py migrate --noinput" + command = f"{python_path} {manage_py} migrate --noinput" preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) logging.InstallLog.writeToFile("Django migrations completed successfully!") @@ -3431,7 +3439,7 @@ password="%s" # Download CDN libraries before collectstatic runs self.downloadCDNLibraries() - command = f"{python_path} manage.py collectstatic --noinput --clear" + command = f"{python_path} {manage_py} collectstatic --noinput --clear" preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) ## Moving static content to lscpd location @@ -5380,16 +5388,29 @@ user_query = SELECT email as user, password, 'vmail' as uid, 'vmail' as gid, '/h os.mkdir(path) # Remove existing key files so ssh-keygen never prompts "Overwrite (y/n)?" - # This keeps unattended / no-confirmation installs fully non-interactive - for f in (key_path, key_pub): - if os.path.exists(f): - try: - os.remove(f) - except OSError: - subprocess.run(["rm", "-f", f], timeout=5, capture_output=True) + # Use shell rm -f so removal is reliable (avoids os.remove permission/state issues) + subprocess.run( + "rm -f /root/.ssh/cyberpanel /root/.ssh/cyberpanel.pub", + shell=True, + timeout=5, + capture_output=True, + ) + # Run ssh-keygen with stdin=input so we never block on "Overwrite (y/n)?" command = "ssh-keygen -f /root/.ssh/cyberpanel -t rsa -N ''" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR, True) + preFlightsChecks.stdOut("Running: %s" % command, 1) + res = subprocess.run( + ["ssh-keygen", "-f", "/root/.ssh/cyberpanel", "-t", "rsa", "-N", ""], + stdin=subprocess.PIPE, + input=b"y\n", + timeout=30, + capture_output=True, + ) + if res.returncode != 0: + err = (res.stderr or b"").decode("utf-8", errors="replace").strip() + preFlightsChecks.stdOut("[ERROR] ssh-keygen failed (return %s). %s" % (res.returncode, err), 1) + return 0 + preFlightsChecks.stdOut("Successfully ran: %s." % command, 1) except BaseException as msg: logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [install_default_keys]") From 9efecec03b621c6cdd7e71064553234983039b5e Mon Sep 17 00:00:00 2001 From: master3395 Date: Wed, 4 Feb 2026 22:26:42 +0100 Subject: [PATCH 09/16] Fix FileNotFoundError for /usr/local/CyberPanel/bin/python on fresh install - install.py: only use system Python or /usr/local/CyberCP/bin/python for migrations; never add /usr/local/CyberPanel paths to candidate list (often missing on fresh install) - install_utils.py: if command uses /usr/local/CyberPanel/bin/python and it does not exist, substitute /usr/bin/python3 and force shell=True so old/cached install.py also works without failing --- install/install.py | 11 ++++------- install/install_utils.py | 9 +++++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/install/install.py b/install/install.py index bfbeb45cd..c087e2f8a 100644 --- a/install/install.py +++ b/install/install.py @@ -3367,19 +3367,16 @@ password="%s" if not self.ensureVirtualEnvironmentSetup(): logging.InstallLog.writeToFile("WARNING: No venv found; will try system Python", 1) - # Find Python: try system Python first so we never use missing /usr/local/CyberPanel/bin/python - # (venv may not exist yet; /usr/local/CyberPanel is legacy and often missing on fresh install) + # Find Python: use only system Python or CyberCP venv (never /usr/local/CyberPanel - often missing on fresh install) python_paths = [ "/usr/bin/python3", "/usr/local/bin/python3", ] if sys.executable and sys.executable not in python_paths: python_paths.append(sys.executable) - python_paths.extend([ - "/usr/local/CyberCP/bin/python", - "/usr/local/CyberPanel/bin/python", - "/usr/local/CyberPanel-venv/bin/python", - ]) + # Only add venv if it exists (avoid FileNotFoundError) + if os.path.isfile("/usr/local/CyberCP/bin/python"): + python_paths.append("/usr/local/CyberCP/bin/python") python_path = None for path in python_paths: diff --git a/install/install_utils.py b/install/install_utils.py index 5549550fc..7a5f65974 100644 --- a/install/install_utils.py +++ b/install/install_utils.py @@ -573,6 +573,15 @@ def call(command, distro, bracket, message, log=0, do_exit=0, code=os.EX_OK, she command = re.sub(r'^(\s*)(?:sudo\s+)?(mysql|mariadb)(\s)', r'\g<1>' + mysql_bin + r'\g<3>', command, count=1) shell = True + # CRITICAL: /usr/local/CyberPanel/bin/python often missing on fresh install; use system Python for manage.py + if '/usr/local/CyberPanel/bin/python' in command and not os.path.isfile('/usr/local/CyberPanel/bin/python'): + fallback = '/usr/bin/python3' + if not os.path.isfile(fallback): + fallback = '/usr/local/bin/python3' + if os.path.isfile(fallback): + command = command.replace('/usr/local/CyberPanel/bin/python', fallback, 1) + shell = True # ensure shell so path with spaces is not split + finalMessage = 'Running: %s' % (message) stdOut(finalMessage, log) count = 0 From 7cab70dbaf55466dd83a8d9d6066e30cf13fcf4e Mon Sep 17 00:00:00 2001 From: master3395 Date: Wed, 4 Feb 2026 22:46:48 +0100 Subject: [PATCH 10/16] Resolve /usr/local/CyberPanel/bin/python on install, upgrade, downgrade Install: - install.py: in preFlightsChecks.call(), replace missing CyberPanel python with /usr/bin/python3 and force shell=True so any code path (including old/cached script) never hits FileNotFoundError. Upgrade: - cyberpanel_upgrade.sh: resolve CP_PYTHON (CyberPanel, CyberCP, python3) before running upgrade.py and configure.py; use it for both. - plogical/upgrade.py: add _python_for_manage(), use it in GeneralMigrations, collectstatic, and upgradePip so migrations/collectstatic work when /usr/local/CyberPanel/bin/python is missing. --- cyberpanel_upgrade.sh | 19 ++++++++++++++++--- install/install.py | 7 +++++++ plogical/upgrade.py | 35 +++++++++++++++-------------------- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/cyberpanel_upgrade.sh b/cyberpanel_upgrade.sh index 601854724..90e545fb8 100644 --- a/cyberpanel_upgrade.sh +++ b/cyberpanel_upgrade.sh @@ -947,10 +947,21 @@ Pre_Upgrade_Branch_Input() { Main_Upgrade() { echo -e "\n[$(date +"%Y-%m-%d %H:%M:%S")] Starting Main_Upgrade function..." | tee -a /var/log/cyberpanel_upgrade_debug.log -echo -e "[$(date +"%Y-%m-%d %H:%M:%S")] Running: /usr/local/CyberPanel/bin/python upgrade.py $Branch_Name" | tee -a /var/log/cyberpanel_upgrade_debug.log + +# Resolve Python for upgrade (avoid FileNotFoundError when /usr/local/CyberPanel/bin/python missing) +CP_PYTHON="" +for py in /usr/local/CyberPanel/bin/python /usr/local/CyberCP/bin/python /usr/bin/python3 /usr/local/bin/python3; do + if [[ -x "$py" ]]; then CP_PYTHON="$py"; break; fi +done +if [[ -z "$CP_PYTHON" ]]; then + echo -e "[$(date +"%Y-%m-%d %H:%M:%S")] ERROR: No Python found for upgrade (tried CyberPanel, CyberCP, python3)" | tee -a /var/log/cyberpanel_upgrade_debug.log + exit 1 +fi +echo -e "[$(date +"%Y-%m-%d %H:%M:%S")] Using Python: $CP_PYTHON" | tee -a /var/log/cyberpanel_upgrade_debug.log +echo -e "[$(date +"%Y-%m-%d %H:%M:%S")] Running: $CP_PYTHON upgrade.py $Branch_Name" | tee -a /var/log/cyberpanel_upgrade_debug.log # Run upgrade.py and capture output -upgrade_output=$(/usr/local/CyberPanel/bin/python upgrade.py "$Branch_Name" 2>&1) +upgrade_output=$("$CP_PYTHON" upgrade.py "$Branch_Name" 2>&1) RETURN_CODE=$? echo "$upgrade_output" | tee -a /var/log/cyberpanel_upgrade_debug.log @@ -1217,7 +1228,9 @@ tar xf wsgi-lsapi-2.1.tgz cd wsgi-lsapi-2.1 || exit echo -e "[$(date +"%Y-%m-%d %H:%M:%S")] Configuring WSGI..." | tee -a /var/log/cyberpanel_upgrade_debug.log -/usr/local/CyberPanel/bin/python ./configure.py 2>&1 | tee -a /var/log/cyberpanel_upgrade_debug.log +PYTHON_CFG="${CP_PYTHON:-/usr/bin/python3}" +[[ -x "$PYTHON_CFG" ]] || PYTHON_CFG="/usr/bin/python3" +"$PYTHON_CFG" ./configure.py 2>&1 | tee -a /var/log/cyberpanel_upgrade_debug.log # Fix Makefile to use proper optimization flags to avoid _FORTIFY_SOURCE warnings echo -e "[$(date +"%Y-%m-%d %H:%M:%S")] Optimizing Makefile for proper compilation..." | tee -a /var/log/cyberpanel_upgrade_debug.log diff --git a/install/install.py b/install/install.py index c087e2f8a..6dba4cce1 100644 --- a/install/install.py +++ b/install/install.py @@ -2839,6 +2839,13 @@ module cyberpanel_ols { # Using shared function from install_utils @staticmethod def call(command, distro, bracket, message, log=0, do_exit=0, code=os.EX_OK, shell=False): + # Fix missing /usr/local/CyberPanel/bin/python on install/upgrade (avoid FileNotFoundError) + if isinstance(command, str) and '/usr/local/CyberPanel/bin/python' in command: + if not os.path.isfile('/usr/local/CyberPanel/bin/python'): + fallback = '/usr/bin/python3' if os.path.isfile('/usr/bin/python3') else '/usr/local/bin/python3' + if os.path.isfile(fallback): + command = command.replace('/usr/local/CyberPanel/bin/python', fallback, 1) + shell = True return install_utils.call(command, distro, bracket, message, log, do_exit, code, shell) def checkIfSeLinuxDisabled(self): diff --git a/plogical/upgrade.py b/plogical/upgrade.py index f96dabe12..5bd9deac8 100644 --- a/plogical/upgrade.py +++ b/plogical/upgrade.py @@ -1715,8 +1715,8 @@ $cfg['Servers'][$i]['LogoutURL'] = 'phpmyadminsignin.php?logout'; cwd = os.getcwd() os.chdir('/usr/local/CyberCP') - - command = '/usr/local/CyberPanel/bin/python manage.py collectstatic --noinput --clear' + py = Upgrade._python_for_manage() + command = py + ' manage.py collectstatic --noinput --clear' Upgrade.executioner(command, 'Remove old static content', 0) os.chdir(cwd) @@ -3084,18 +3084,27 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL except: pass + @staticmethod + def _python_for_manage(): + """Resolve Python for manage.py (avoid FileNotFoundError when /usr/local/CyberPanel/bin/python missing).""" + for path in ('/usr/local/CyberPanel/bin/python', '/usr/local/CyberCP/bin/python', '/usr/bin/python3', '/usr/local/bin/python3'): + if path and os.path.isfile(path) and os.access(path, os.X_OK): + return path + return '/usr/bin/python3' + @staticmethod def GeneralMigrations(): try: cwd = os.getcwd() os.chdir('/usr/local/CyberCP') + py = Upgrade._python_for_manage() - command = '/usr/local/CyberPanel/bin/python manage.py makemigrations' + command = py + ' manage.py makemigrations' Upgrade.executioner(command, 'python manage.py makemigrations', 0) - command = '/usr/local/CyberPanel/bin/python manage.py makemigrations' - Upgrade.executioner(command, '/usr/local/CyberPanel/bin/python manage.py migrate', 0) + command = py + ' manage.py makemigrations' + Upgrade.executioner(command, py + ' manage.py migrate', 0) os.chdir(cwd) @@ -4606,21 +4615,7 @@ echo $oConfig->Save() ? 'Done' : 'Error'; """Upgrade pip to latest version for better package compatibility""" try: Upgrade.stdOut("Upgrading pip to latest version...", 1) - - # Determine the correct Python path - python_paths = [ - "/usr/local/CyberPanel/bin/python", - "/usr/local/CyberCP/bin/python", - "/usr/bin/python3", - "/usr/local/bin/python3" - ] - - python_path = None - for path in python_paths: - if os.path.exists(path): - python_path = path - break - + python_path = Upgrade._python_for_manage() if not python_path: Upgrade.stdOut("No Python executable found for pip upgrade", 0) return False From 2388d68f9faece7a0252a2c7d5b3eba423da5ef7 Mon Sep 17 00:00:00 2001 From: master3395 Date: Wed, 4 Feb 2026 23:19:22 +0100 Subject: [PATCH 11/16] Install/upgrade fixes: stdin for pipe, branch archive, Python/composer paths, MariaDB version, web server order; FTP quota and docs --- cyberpanel.sh | 41 +++- install/install.py | 63 ++---- plogical/applicationInstaller.py | 29 +-- plogical/upgrade.py | 32 ++-- to-do/AWS-CURSOR-REMOTE-SSH-SETUP.md | 120 ++++++++++++ to-do/FTP-QUOTA-BROWSER-TEST-CHECKLIST.md | 21 ++ to-do/INSTALL-UPGRADE-DOWNGRADE-COMMANDS.md | 180 ++++++++++++++++++ to-do/PURE-FTPD-QUOTA-SYNTAX-FIX.md | 21 ++ to-do/V2.5.5-DEV-BRANCH-COMPATIBILITY.md | 76 ++++++++ .../websiteFunctions/ftpQuotaManagement.html | 53 +++++- websiteFunctions/urls.py | 1 + websiteFunctions/views.py | 8 + websiteFunctions/website.py | 129 ++++++++++++- 13 files changed, 692 insertions(+), 82 deletions(-) create mode 100644 to-do/AWS-CURSOR-REMOTE-SSH-SETUP.md create mode 100644 to-do/FTP-QUOTA-BROWSER-TEST-CHECKLIST.md create mode 100644 to-do/INSTALL-UPGRADE-DOWNGRADE-COMMANDS.md create mode 100644 to-do/V2.5.5-DEV-BRANCH-COMPATIBILITY.md diff --git a/cyberpanel.sh b/cyberpanel.sh index 4607b698f..c801e9cbd 100644 --- a/cyberpanel.sh +++ b/cyberpanel.sh @@ -580,7 +580,37 @@ cleanup_existing_cyberpanel() { # Function to install CyberPanel directly using the working method install_cyberpanel_direct() { - # Ask MariaDB version first (even in no-confirmation/auto mode) if not set via --mariadb-version + # Ask web server (OpenLiteSpeed vs LiteSpeed Enterprise) BEFORE MariaDB; default OpenLiteSpeed + if [ -z "$LS_ENT" ]; then + if [ "$AUTO_INSTALL" = true ]; then + LS_ENT="" + echo " Using OpenLiteSpeed (auto mode)." + else + echo "" + echo " Web server: 1) OpenLiteSpeed (default), 2) LiteSpeed Enterprise" + read -r -t 60 -p " Enter 1 or 2 [1]: " LS_CHOICE || true + LS_CHOICE="${LS_CHOICE:-1}" + LS_CHOICE="${LS_CHOICE// /}" + if [ "$LS_CHOICE" = "2" ]; then + echo " LiteSpeed Enterprise selected. Enter serial/key (required):" + read -r -t 120 -p " Serial: " LS_SERIAL || true + LS_SERIAL="${LS_SERIAL:-}" + if [ -z "$LS_SERIAL" ]; then + echo " No serial provided. Defaulting to OpenLiteSpeed." + LS_ENT="" + else + LS_ENT="ent" + echo " Using LiteSpeed Enterprise with provided serial." + fi + else + LS_ENT="" + echo " Using OpenLiteSpeed." + fi + echo "" + fi + fi + + # Ask MariaDB version (after web server choice) if not set via --mariadb-version if [ -z "$MARIADB_VER" ]; then echo "" echo " MariaDB version: 10.11, 11.8 (LTS, default) or 12.1?" @@ -1090,7 +1120,10 @@ except: # install.py requires publicip as first positional argument local install_args=("$server_ip") - # Add optional arguments based on user preferences + # Web server: OpenLiteSpeed (default) or LiteSpeed Enterprise (--ent + --serial) + if [ -n "$LS_ENT" ] && [ -n "$LS_SERIAL" ]; then + install_args+=("--ent" "$LS_ENT" "--serial" "$LS_SERIAL") + fi # Default: OpenLiteSpeed, Full installation (postfix, powerdns, ftp), Local MySQL install_args+=("--postfix" "ON") install_args+=("--powerdns" "ON") @@ -2686,9 +2719,9 @@ parse_arguments() { echo "Options:" echo " -b, --branch BRANCH Install from specific branch/commit" echo " -v, --version VER Install specific version (auto-adds v prefix)" - echo " --mariadb-version VER MariaDB version: 10.11, 11.8 or 12.1 (asked first if omitted)" + echo " --mariadb-version VER MariaDB version: 10.11, 11.8 or 12.1 (asked after web server)" echo " --debug Enable debug mode" - echo " --auto Auto mode without prompts (MariaDB still asked first unless --mariadb-version)" + echo " --auto Auto mode: OpenLiteSpeed + MariaDB 11.8 unless --mariadb-version set" echo " -h, --help Show this help message" echo "" echo "Examples:" diff --git a/install/install.py b/install/install.py index 6dba4cce1..a932a1620 100644 --- a/install/install.py +++ b/install/install.py @@ -405,48 +405,11 @@ class preFlightsChecks: except Exception as e: self.stdOut("Warning: Could not remove compat packages: " + str(e), 0) - # Check if MariaDB is already installed before attempting installation + # Do NOT install MariaDB here with plain dnf (that would install distro default 10.11). + # installMySQL() runs later with the user's chosen version (--mariadb-version: 10.11, 11.8 or 12.1). is_installed, installed_version, major_minor = self.checkExistingMariaDB() - if is_installed: - self.stdOut(f"MariaDB/MySQL is already installed (version: {installed_version}), skipping installation", 1) - mariadb_installed = True - else: - # Install MariaDB with enhanced AlmaLinux 9.6 support - self.stdOut("Installing MariaDB for AlmaLinux 9.6...", 1) - - # Try multiple installation methods for maximum compatibility - mariadb_commands = [ - "dnf install -y mariadb-server mariadb-devel mariadb-client --skip-broken --nobest", - "dnf install -y mariadb-server mariadb-devel mariadb-client --allowerasing", - "dnf install -y mariadb-server mariadb-devel --skip-broken --nobest --allowerasing", - "dnf install -y mariadb-server --skip-broken --nobest --allowerasing" - ] - - mariadb_installed = False - for cmd in mariadb_commands: - try: - result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=300) - if result.returncode == 0: - mariadb_installed = True - self.stdOut(f"MariaDB installed successfully with command: {cmd}", 1) - break - except subprocess.TimeoutExpired: - self.stdOut(f"Timeout installing MariaDB with command: {cmd}", 0) - continue - except Exception as e: - self.stdOut(f"Error installing MariaDB with command: {cmd} - {str(e)}", 0) - continue - - if not mariadb_installed: - self.stdOut("MariaDB installation failed, trying MySQL as fallback...", 0) - try: - command = "dnf install -y mysql-server mysql-devel --skip-broken --nobest --allowerasing" - self.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - self.stdOut("MySQL installed as fallback for MariaDB", 1) - mariadb_installed = True - except: - self.stdOut("Both MariaDB and MySQL installation failed", 0) + self.stdOut(f"MariaDB/MySQL already installed (version: {installed_version}), skipping", 1) # Install additional required packages self.stdOut("Installing additional required packages...", 1) @@ -5804,18 +5767,22 @@ milter_default_action = accept os.chdir(self.cwd) - # Download composer.sh if missing (e.g. when run from temp dir without repo file) - composer_sh = os.path.join(self.cwd, "composer.sh") - if not os.path.exists(composer_sh) or not os.path.isfile(composer_sh): + # Download composer.sh to a known path so chmod never fails with "cannot access 'composer.sh'" + composer_sh = "/tmp/composer.sh" + if not os.path.isfile(composer_sh): command = "wget -q https://cyberpanel.sh/composer.sh -O " + composer_sh preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - - if os.path.exists(composer_sh): - command = "chmod +x " + composer_sh + if not os.path.isfile(composer_sh): + command = "curl -sSL https://cyberpanel.sh/composer.sh -o " + composer_sh preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - command = "bash " + os.path.abspath(composer_sh) - preFlightsChecks.call(command, self.distro, "./composer.sh", command, 1, 0, os.EX_OSERR, True) + if os.path.isfile(composer_sh): + command = "chmod +x " + composer_sh + preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + command = "bash " + composer_sh + preFlightsChecks.call(command, self.distro, "composer.sh", command, 1, 0, os.EX_OSERR, True) + else: + logging.InstallLog.writeToFile("composer.sh download failed, skipping [setupPHPAndComposer]", 0) except OSError as msg: logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [setupPHPAndComposer]") diff --git a/plogical/applicationInstaller.py b/plogical/applicationInstaller.py index c3b89fbc6..9c707099b 100644 --- a/plogical/applicationInstaller.py +++ b/plogical/applicationInstaller.py @@ -101,19 +101,24 @@ class ApplicationInstaller(multi.Thread): @staticmethod def setupComposer(): - - if os.path.exists('composer.sh'): - os.remove('composer.sh') - + composer_sh = '/tmp/composer.sh' if not os.path.exists('/usr/bin/composer'): - command = "wget https://cyberpanel.sh/composer.sh" - ProcessUtilities.executioner(command, 'root', True) - - command = "chmod +x composer.sh" - ProcessUtilities.executioner(command, 'root', True) - - command = "./composer.sh" - ProcessUtilities.executioner(command, 'root', True) + try: + if os.path.exists(composer_sh): + os.remove(composer_sh) + command = "wget -q https://cyberpanel.sh/composer.sh -O " + composer_sh + ProcessUtilities.executioner(command, 'root', True) + if not os.path.isfile(composer_sh): + command = "curl -sSL https://cyberpanel.sh/composer.sh -o " + composer_sh + ProcessUtilities.executioner(command, 'root', True) + if not os.path.isfile(composer_sh): + return + command = "chmod +x " + composer_sh + ProcessUtilities.executioner(command, 'root', True) + command = "bash " + composer_sh + ProcessUtilities.executioner(command, 'root', True) + except Exception: + pass def InstallNodeJS(self): diff --git a/plogical/upgrade.py b/plogical/upgrade.py index 5bd9deac8..43c7871fb 100644 --- a/plogical/upgrade.py +++ b/plogical/upgrade.py @@ -1307,18 +1307,26 @@ $cfg['Servers'][$i]['LogoutURL'] = 'phpmyadminsignin.php?logout'; @staticmethod def setupComposer(): - - if os.path.exists('composer.sh'): - os.remove('composer.sh') - - command = "wget https://cyberpanel.sh/composer.sh" - Upgrade.executioner(command, 0) - - command = "chmod +x composer.sh" - Upgrade.executioner(command, 0) - - command = "./composer.sh" - Upgrade.executioner(command, 0) + composer_sh = '/tmp/composer.sh' + try: + if os.path.exists(composer_sh): + os.remove(composer_sh) + # Download to known path so chmod/run work regardless of cwd + command = "wget -q https://cyberpanel.sh/composer.sh -O " + composer_sh + Upgrade.executioner(command, 0) + if not os.path.isfile(composer_sh): + command = "curl -sSL https://cyberpanel.sh/composer.sh -o " + composer_sh + Upgrade.executioner(command, 0) + if not os.path.isfile(composer_sh): + Upgrade.stdOut("composer.sh download failed, skipping", 0) + return + command = "chmod +x " + composer_sh + Upgrade.executioner(command, 0) + command = "bash " + composer_sh + Upgrade.executioner(command, 0) + except Exception as e: + ErrorSanitizer.log_error_securely(e, 'setupComposer') + Upgrade.stdOut("setupComposer error (non-fatal)", 0) @staticmethod def downoad_and_install_raindloop(): diff --git a/to-do/AWS-CURSOR-REMOTE-SSH-SETUP.md b/to-do/AWS-CURSOR-REMOTE-SSH-SETUP.md new file mode 100644 index 000000000..b62b92e8e --- /dev/null +++ b/to-do/AWS-CURSOR-REMOTE-SSH-SETUP.md @@ -0,0 +1,120 @@ +# AWS EC2 + Cursor Remote-SSH – Full Setup Guide + +Use this guide to get **aws-server** (3.144.171.128) working with Cursor Remote-SSH. +Do the steps in order. Everything is copy-paste ready. + +--- + +## 1. Windows SSH config + +**File:** `C:\Users\kimsk\.ssh\config` + +- Open the file in Notepad or Cursor. +- Find the `Host aws-server` block and replace it entirely with the block below (or add it if missing). +- Use **straight double quotes** `"`, not curly quotes. Path uses forward slashes to avoid issues. + +**Exact block to use (port 22 – default):** + +``` +Host aws-server + HostName 3.144.171.128 + User ec2-user + Port 22 + IdentityFile "D:/OneDrive - v-man/Priv/VPS/Cyberpanel.pem" +``` + +- Save and close. +- If you later confirm SSH on the instance is on port 2222, change `Port 22` to `Port 2222` and add an inbound rule for 2222 in the Security Group (see step 3). + +--- + +## 2. AWS Security Group – allow SSH (port 22) + +1. **AWS Console** → **EC2** → **Instances**. +2. Select the instance whose **Public IPv4** is **3.144.171.128**. +3. Open the **Security** tab → click the **Security group** name (e.g. `sg-xxxxx`). +4. **Edit inbound rules** → **Add rule**: + - **Type:** SSH + - **Port:** 22 + - **Source:** **My IP** (recommended) or **Anywhere-IPv4** (`0.0.0.0/0`) for testing only. +5. **Save rules**. + +If you use port 2222 on the instance, add another rule: **Custom TCP**, port **2222**, source **My IP** (or **Anywhere-IPv4** for testing). + +--- + +## 3. Start SSH on the instance (fix “Connection refused”) + +You must run commands on the instance without using SSH from your PC. Use one of these. + +### Option A: EC2 Instance Connect (simplest) + +1. **EC2** → **Instances** → select the instance (3.144.171.128). +2. Click **Connect**. +3. Open the **EC2 Instance Connect** tab → **Connect** (browser shell). + +In the browser terminal, run: + +```bash +sudo systemctl status sshd +sudo systemctl start sshd +sudo systemctl enable sshd +sudo ss -tlnp | grep 22 +``` + +You should see `sshd` listening on port 22. Then close the browser and try Cursor. + +### Option B: Session Manager + +1. **EC2** → **Instances** → select the instance → **Connect**. +2. Choose **Session Manager** → **Connect**. +3. Run the same commands as in Option A. + +### Option C: SSH is on port 2222 + +If you know SSH was moved to 2222 on this instance: + +1. In the Security Group, add an **inbound rule**: **Custom TCP**, port **2222**, source **My IP** (or **Anywhere-IPv4** for testing). +2. In your SSH config, set `Port 2222` for `aws-server` (see step 1). +3. Test (see step 4). + +--- + +## 4. Test from Windows + +Open **PowerShell** and run: + +```powershell +ssh -i "D:/OneDrive - v-man/Priv/VPS/Cyberpanel.pem" -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new ec2-user@3.144.171.128 +``` + +- If it asks for a host key, type `yes`. +- If you get a shell prompt, SSH works. Type `exit` to close. +- If you get **Connection refused**: SSH is not listening on 22 (or 2222); repeat step 3 (Instance Connect / Session Manager) and ensure `sshd` is running and listening on the port you use. +- If you get **Connection timed out**: Security Group is still blocking the port; recheck step 2 and that you edited the security group attached to this instance. + +--- + +## 5. Connect from Cursor + +1. In Cursor: **Ctrl+Shift+P** (or **Cmd+Shift+P** on Mac) → **Remote-SSH: Connect to Host**. +2. Choose **aws-server** (or type `aws-server`). +3. Wait for the remote window to open. Cursor AI (Chat, Composer) works in that window as usual. + +--- + +## Checklist + +- [ ] SSH config has the `aws-server` block with correct `IdentityFile` and `Port` (22 or 2222). +- [ ] Security Group has an inbound rule for the SSH port (22 or 2222) from My IP (or 0.0.0.0/0 for testing). +- [ ] `sshd` is running on the instance (started via Instance Connect or Session Manager). +- [ ] `ssh ... ec2-user@3.144.171.128` works in PowerShell. +- [ ] Cursor **Connect to Host** → **aws-server** succeeds. + +--- + +## If it still fails + +- **Connection refused** → Instance side: start/enable `sshd` and confirm it listens on the port you use (step 3). +- **Connection timed out** → Network: open that port in the instance’s Security Group (step 2). +- **Permission denied (publickey)** → Wrong key or user: confirm the .pem is the one for this instance and the user is `ec2-user` (Amazon Linux) or `ubuntu` (Ubuntu AMI). diff --git a/to-do/FTP-QUOTA-BROWSER-TEST-CHECKLIST.md b/to-do/FTP-QUOTA-BROWSER-TEST-CHECKLIST.md new file mode 100644 index 000000000..f9250e0fd --- /dev/null +++ b/to-do/FTP-QUOTA-BROWSER-TEST-CHECKLIST.md @@ -0,0 +1,21 @@ +# FTP Quota Management – Browser Test Checklist + +Use after deploying latest code. Open: `/ftp/quotaManagement` + +## 1. Page load – status +- **Pure-FTPd stopped:** Yellow warning "Pure-FTPd is not running. Please enable Pure-FTPd first (Server Status → Services)..." and Enable button disabled/hidden. +- **Pure-FTPd running, quota on:** Green "FTP Quota system is already enabled"; button disabled. +- **Pure-FTPd running, quota off:** Blue info and enabled "Enable FTP Quota System" button. + +## 2. Click Enable +- If FTP was running: success message and UI switches to "already enabled". No "Pure-FTPd did not start" error. +- If FTP was stopped: API returns "Pure-FTPd is not running. Please enable Pure-FTPd first...". + +## 3. Table +- Quotas table loads; Refresh works. + +## 4. One-time fix on server (if needed) +```bash +sudo sed -i 's/^Quota.*/Quota 100000:100000/' /etc/pure-ftpd/pure-ftpd.conf +sudo systemctl start pure-ftpd +``` diff --git a/to-do/INSTALL-UPGRADE-DOWNGRADE-COMMANDS.md b/to-do/INSTALL-UPGRADE-DOWNGRADE-COMMANDS.md new file mode 100644 index 000000000..7bb0a5017 --- /dev/null +++ b/to-do/INSTALL-UPGRADE-DOWNGRADE-COMMANDS.md @@ -0,0 +1,180 @@ +# CyberPanel Install, Upgrade, and Downgrade Commands + +Reference for all standard and branch-specific install/upgrade/downgrade commands (master3395 fork and upstream). + +--- + +## Fresh install + +### One-liner (official / upstream) + +```bash +sh <(curl https://cyberpanel.net/install.sh) +``` + +### One-liner with sudo (if not root) + +```bash +curl -sO https://cyberpanel.net/install.sh && sudo bash install.sh +# or +curl -sL https://cyberpanel.net/install.sh | sudo bash -s -- +``` + +### Install from master3395 fork (this repo) + +**Stable:** + +```bash +curl -sL https://raw.githubusercontent.com/master3395/cyberpanel/stable/cyberpanel.sh | sudo bash -s -- +``` + +**Development (v2.5.5-dev):** + +```bash +curl -sL https://raw.githubusercontent.com/master3395/cyberpanel/v2.5.5-dev/cyberpanel.sh | sudo bash -s -- -b v2.5.5-dev +``` + +### Install with branch/version options + +```bash +# Download script first (recommended so -b/-v work reliably) +curl -sL -o cyberpanel.sh https://raw.githubusercontent.com/master3395/cyberpanel/v2.5.5-dev/cyberpanel.sh +chmod +x cyberpanel.sh +sudo bash cyberpanel.sh [OPTIONS] +``` + +**Options:** + +| Option | Example | Description | +|--------|---------|-------------| +| `-b BRANCH` / `--branch BRANCH` | `-b v2.5.5-dev` | Install from branch or tag | +| `-v VER` / `--version VER` | `-v 2.5.5-dev` | Version (script adds `v` prefix as needed) | +| `--mariadb-version VER` | `--mariadb-version 10.11` | MariaDB: `10.11`, `11.8`, or `12.1` | +| `--auto` | `--auto` | Non-interactive (still asks MariaDB unless `--mariadb-version` is set) | +| `--debug` | `--debug` | Debug mode | + +**Examples:** + +```bash +sudo bash cyberpanel.sh # Interactive +sudo bash cyberpanel.sh -b v2.5.5-dev # Development branch +sudo bash cyberpanel.sh -v 2.5.5-dev # Same as above (v prefix added) +sudo bash cyberpanel.sh -v 2.4.4 # Install 2.4.4 +sudo bash cyberpanel.sh -b main # From main branch +sudo bash cyberpanel.sh -b a1b2c3d4 # From specific commit hash +sudo bash cyberpanel.sh --mariadb-version 10.11 # MariaDB 10.11 +sudo bash cyberpanel.sh --mariadb-version 12.1 # MariaDB 12.1 +sudo bash cyberpanel.sh --auto --mariadb-version 11.8 # Fully non-interactive, MariaDB 11.8 +sudo bash cyberpanel.sh --debug # Debug +``` + +--- + +## Upgrade (existing CyberPanel) + +### One-liner upgrade to latest stable + +```bash +bash <(curl -sL https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/cyberpanel_upgrade.sh) +``` + +### Upgrade to a specific branch/version (upstream) + +```bash +bash <(curl -sL https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/cyberpanel_upgrade.sh) -b v2.5.5-dev +bash <(curl -sL https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/cyberpanel_upgrade.sh) -b 2.4.4 +``` + +### Upgrade using master3395 fork + +```bash +sudo bash <(curl -sL https://raw.githubusercontent.com/master3395/cyberpanel/stable/cyberpanel_upgrade.sh) -b v2.5.5-dev +``` + +Or download then run: + +```bash +curl -sL -o cyberpanel_upgrade.sh https://raw.githubusercontent.com/master3395/cyberpanel/v2.5.5-dev/cyberpanel_upgrade.sh +chmod +x cyberpanel_upgrade.sh +sudo bash cyberpanel_upgrade.sh -b v2.5.5-dev +``` + +**Upgrade options:** + +| Option | Example | Description | +|--------|---------|-------------| +| `-b BRANCH` / `--branch BRANCH` | `-b v2.5.5-dev` | Upgrade to this branch/tag | +| `--no-system-update` | (optional) | Skip full `yum/dnf update -y` (faster if system is already updated) | + +**Examples:** + +```bash +sudo bash cyberpanel_upgrade.sh -b v2.5.5-dev +sudo bash cyberpanel_upgrade.sh -b 2.4.4 +sudo bash cyberpanel_upgrade.sh -b stable +sudo bash cyberpanel_upgrade.sh -b v2.5.5-dev --no-system-update +``` + +--- + +## Downgrade + +Downgrade is done by running the **upgrade** script with the **older** branch/version. + +### Downgrade to 2.4.4 (or another older version) + +```bash +sudo bash <(curl -sL https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/cyberpanel_upgrade.sh) -b 2.4.4 +``` + +Or with master3395 fork: + +```bash +curl -sL -o cyberpanel_upgrade.sh https://raw.githubusercontent.com/master3395/cyberpanel/stable/cyberpanel_upgrade.sh +chmod +x cyberpanel_upgrade.sh +sudo bash cyberpanel_upgrade.sh -b 2.4.4 +``` + +### Downgrade from v2.5.5-dev to stable + +```bash +sudo bash cyberpanel_upgrade.sh -b stable +``` + +--- + +## Pre-upgrade (download upgrade script only) + +From the interactive menu: **Option 5 – Pre-Upgrade**. + +Or manually: + +```bash +# Download latest upgrade script to /usr/local/ +curl -sL -o /usr/local/cyberpanel_upgrade.sh https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/cyberpanel_upgrade.sh +chmod 700 /usr/local/cyberpanel_upgrade.sh + +# Run when ready +sudo /usr/local/cyberpanel_upgrade.sh -b v2.5.5-dev +``` + +--- + +## Quick reference + +| Action | Command | +|--------|---------| +| **Install (official)** | `sh <(curl https://cyberpanel.net/install.sh)` | +| **Install stable (master3395)** | `curl -sL https://raw.githubusercontent.com/master3395/cyberpanel/stable/cyberpanel.sh \| sudo bash -s --` | +| **Install v2.5.5-dev** | `curl -sL https://raw.githubusercontent.com/master3395/cyberpanel/v2.5.5-dev/cyberpanel.sh \| sudo bash -s -- -b v2.5.5-dev` | +| **Upgrade to v2.5.5-dev** | `sudo bash <(curl -sL https://raw.githubusercontent.com/master3395/cyberpanel/stable/cyberpanel_upgrade.sh) -b v2.5.5-dev` | +| **Upgrade to 2.4.4** | `sudo bash <(curl -sL .../cyberpanel_upgrade.sh) -b 2.4.4` | +| **Downgrade to 2.4.4** | Same as upgrade: `... cyberpanel_upgrade.sh -b 2.4.4` | + +--- + +## Notes + +- Run as **root** or with **sudo**; if using `curl | sudo bash`, use `bash -s --` and put branch/options after `--` so they are passed to the script. +- MariaDB version can be set at install with `--mariadb-version 10.11`, `11.8`, or `12.1`. +- Upgrade script branch: `-b v2.5.5-dev`, `-b 2.4.4`, `-b stable`, or `-b `. diff --git a/to-do/PURE-FTPD-QUOTA-SYNTAX-FIX.md b/to-do/PURE-FTPD-QUOTA-SYNTAX-FIX.md index 917206ec1..09dba530a 100644 --- a/to-do/PURE-FTPD-QUOTA-SYNTAX-FIX.md +++ b/to-do/PURE-FTPD-QUOTA-SYNTAX-FIX.md @@ -19,6 +19,27 @@ The config used `Quota yes`, but Pure-FTPd expects **`Quota maxfiles:maxsize`** - **install/pure-ftpd/pure-ftpd.conf** and **install/pure-ftpd-one/pure-ftpd.conf**: `Quota yes` → `Quota 100000:100000`. - **websiteFunctions/website.py** (`enableFTPQuota`): sed/echo now write `Quota 100000:100000` instead of `Quota yes` (or tabs). +## One-time fix on server (if "Enable" still breaks it) +Run on the server as root (copy script from repo or run inline): + +**Option A – script (repo root: `fix-pureftpd-quota-once.sh`):** +```bash +sudo bash /path/to/fix-pureftpd-quota-once.sh +``` + +**Option B – inline:** +```bash +sudo sed -i 's/^Quota.*/Quota 100000:100000/' /etc/pure-ftpd/pure-ftpd.conf +# If TLS 1 is set but cert missing, disable TLS: +sudo sed -i 's/^TLS[[:space:]]*1/TLS 0/' /etc/pure-ftpd/pure-ftpd.conf +sudo systemctl start pure-ftpd +``` +Then deploy the latest panel code so "Enable" uses the correct Quota syntax. + +## Code safeguards (enableFTPQuota) +- **Backup before modify**: Timestamped backup of `pure-ftpd.conf` and `pureftpd-mysql.conf` before any change. +- **Safety net before restart**: If the Quota line is not valid (`Quota maxfiles:maxsize`), it is corrected to `Quota 100000:100000` so Pure-FTPd never gets an invalid line on restart. + ## Reference - Upstream: https://github.com/jedisct1/pure-ftpd/blob/master/pure-ftpd.conf.in (comment: "Quota 1000:10"). - `pure-ftpd --help`: `-n --quota `. diff --git a/to-do/V2.5.5-DEV-BRANCH-COMPATIBILITY.md b/to-do/V2.5.5-DEV-BRANCH-COMPATIBILITY.md new file mode 100644 index 000000000..733f3c90a --- /dev/null +++ b/to-do/V2.5.5-DEV-BRANCH-COMPATIBILITY.md @@ -0,0 +1,76 @@ +# v2.5.5-dev Branch Compatibility Check + +**Date:** 2026-02-04 +**Branches compared:** [v2.5.5-dev](https://github.com/master3395/cyberpanel/tree/v2.5.5-dev) vs [v2.4.4](https://github.com/master3395/cyberpanel/tree/v2.4.4) vs [stable](https://github.com/master3395/cyberpanel/tree/stable) + +--- + +## 1. Will your v2.5.5-dev changes work? + +**Yes.** The Ban IP / Firewall Banned IPs changes on v2.5.5-dev are self-contained and consistent: + +| Component | Status | +|-----------|--------| +| **plogical/firewallUtilities.py** | `blockIP` / `unblockIP` use `result == 1` for success; log file path is `/usr/local/CyberCP/data/blocked_ips.log` (writable by `cyberpanel`). | +| **plogical/processUtilities.py** | When running as root, `executioner` uses `normalExecutioner` return value (1 = success, 0 = fail). | +| **firewall/firewallManager.py** | `addBannedIP` uses `FirewallUtilities.blockIP`; ACL and errors return JSON with `error_message` and `error`; rollback on block failure. | +| **firewall/views.py** | `addBannedIP` parses JSON body and calls `fm.addBannedIP(userID, request_data)`. | +| **firewall/urls.py** | Routes `getBannedIPs`, `addBannedIP`, `modifyBannedIP`, `removeBannedIP`, `deleteBannedIP`, `exportBannedIPs`, `importBannedIPs` are present. | +| **baseTemplate/views.py** | `blockIPAddress` uses `FirewallUtilities.blockIP` (no subprocess). | +| **baseTemplate (homePage + system-status.js)** | Ban IP calls `/firewall/addBannedIP` with `ip`, `reason`, `duration`; shows server `error_message` in notifications. | + +**Deployment requirements (already applied on your server):** + +- `/usr/local/CyberCP/data` owned by `cyberpanel:cyberpanel` (for `banned_ips.json` and `blocked_ips.log`). +- Deploy updated files from v2.5.5-dev into `/usr/local/CyberCP/` and restart `lscpd`. + +--- + +## 2. Does v2.5.5-dev have all functions from v2.4.4 and stable? + +**Summary:** + +- **v2.5.5-dev has more than v2.4.4 and stable** in terms of features (Banned IPs, FTP quotas, email limits, user management, bandwidth management, etc.). It is a development branch built on top of the same base. +- **v2.5.5-dev is missing a few items that exist only on stable** (backports or stable-only fixes). Nothing critical for the Ban IP feature; mainly scripts and tests. + +### v2.5.5-dev has everything from v2.4.4 that matters + +- v2.4.4 is older (fewer commits). v2.5.5-dev contains the same core apps (firewall, baseTemplate, loginSystem, backup, etc.) plus many additions. +- **Firewall:** v2.4.4 has no Banned IPs routes; v2.5.5-dev adds the full Banned IPs feature (getBannedIPs, addBannedIP, modify, remove, delete, export, import). + +### v2.5.5-dev vs stable + +- **Stable has ~86 files that differ from v2.5.5-dev**, including: + - **run_migration.py** – present on stable, **not** on v2.5.5-dev (migration helper). + - **test_firewall_blocking.py** – test script on stable. + - **rollback_phpmyadmin_redirect.sh** – rollback script on stable. + - **install/**, **plogical/** (e.g. mysqlUtilities, upgrade, backup, sslUtilities), **pluginInstaller** – some fixes/improvements on stable that may not be in v2.5.5-dev. + +- **v2.5.5-dev has 3652+ files changed vs stable** – it has many more features (user management, website functions, bandwidth, FTP quotas, email limits, Banned IPs, etc.). + +So: + +- **Feature parity:** v2.5.5-dev has **all the main functions** from v2.4.4 and **adds** Banned IPs and other features. It does **not** lack core features that v2.4.4 or stable have. +- **Stable-only extras:** Stable has a few **extra** scripts/fixes (e.g. `run_migration.py`, `rollback_phpmyadmin_redirect.sh`, some plogical/install changes). If you need those, you can cherry-pick or merge from stable into v2.5.5-dev. + +--- + +## 3. Directory layout comparison + +| In stable, not in v2.5.5-dev (by name) | In v2.5.5-dev, not in stable | +|----------------------------------------|------------------------------| +| emailMarketing (or different layout) | bin, docs, modules, public, sql, test, to-do | +| examplePlugin | (v2.5.5-dev has more structure) | +| guides | | +| scripts | | +| testPlugin | test (different name) | + +Your current repo (v2.5.5-dev) includes `emailMarketing`, `websiteFunctions`, `firewall`, `baseTemplate`, etc. The diff is mostly naming (e.g. test vs testPlugin) and stable having a few extra scripts/docs. + +--- + +## 4. Recommendation + +1. **Use v2.5.5-dev as-is for Ban IP and current features** – the changes are consistent and will work with the deployment steps above. +2. **Periodically merge or cherry-pick from stable** into v2.5.5-dev if you want stable’s migration script, phpMyAdmin rollback script, and any plogical/install fixes. +3. **You do have all the functions from v2.4.4 and stable** in the sense of core product behavior; v2.5.5-dev adds more (Banned IPs, etc.) and is only missing some optional stable-only scripts/fixes. diff --git a/websiteFunctions/templates/websiteFunctions/ftpQuotaManagement.html b/websiteFunctions/templates/websiteFunctions/ftpQuotaManagement.html index b075aa0fd..c29232e44 100644 --- a/websiteFunctions/templates/websiteFunctions/ftpQuotaManagement.html +++ b/websiteFunctions/templates/websiteFunctions/ftpQuotaManagement.html @@ -38,7 +38,15 @@ FTP Quota Management - CyberPanel
-
+ + +
FTP Quota System

Enable and manage individual FTP user quotas. This allows you to set storage limits for each FTP user.