diff --git a/ModuleDeveloperGuide.pdf b/ModuleDeveloperGuide.pdf
new file mode 100644
index 000000000..f9572aa76
Binary files /dev/null and b/ModuleDeveloperGuide.pdf differ
diff --git a/baseTemplate/templates/baseTemplate/index.html b/baseTemplate/templates/baseTemplate/index.html
index 742a8f49f..5c32520af 100644
--- a/baseTemplate/templates/baseTemplate/index.html
+++ b/baseTemplate/templates/baseTemplate/index.html
@@ -805,10 +805,195 @@
.notification-shown.ai-scanner-shown #main-content {
padding-top: 220px;
}
-
+
.notification-shown .ai-scanner-banner {
top: 130px;
}
+
+ /* .htaccess Feature Banner */
+ .htaccess-feature-banner {
+ position: fixed;
+ top: 80px;
+ left: 260px;
+ right: 0;
+ background: linear-gradient(135deg, #10b981 0%, #059669 50%, #047857 100%);
+ border-bottom: 2px solid #065f46;
+ padding: 18px 30px;
+ z-index: 997;
+ box-shadow: 0 6px 25px rgba(16, 185, 129, 0.3);
+ animation: slideDown 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
+ display: none;
+ }
+
+ .htaccess-feature-banner.show {
+ display: block;
+ }
+
+ .htaccess-content {
+ display: flex;
+ align-items: center;
+ gap: 1.5rem;
+ max-width: 1400px;
+ margin: 0 auto;
+ }
+
+ .htaccess-icon {
+ background: rgba(255, 255, 255, 0.25);
+ border-radius: 14px;
+ padding: 14px;
+ backdrop-filter: blur(12px);
+ border: 1px solid rgba(255, 255, 255, 0.4);
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+ }
+
+ .htaccess-icon i {
+ color: white;
+ font-size: 1.75rem;
+ display: block;
+ }
+
+ .htaccess-text {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+ }
+
+ .htaccess-main-text {
+ color: white;
+ font-size: 1.1rem;
+ font-weight: 700;
+ line-height: 1.4;
+ letter-spacing: 0.3px;
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ }
+
+ .htaccess-sub-text {
+ color: rgba(255, 255, 255, 0.9);
+ font-size: 0.875rem;
+ font-weight: 500;
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ }
+
+ .htaccess-sub-text span {
+ display: flex;
+ align-items: center;
+ gap: 0.4rem;
+ }
+
+ .htaccess-sub-text i {
+ font-size: 0.75rem;
+ }
+
+ .htaccess-actions {
+ display: flex;
+ gap: 12px;
+ }
+
+ .htaccess-btn {
+ background: white;
+ color: #047857;
+ padding: 14px 28px;
+ border-radius: 10px;
+ text-decoration: none;
+ font-weight: 700;
+ font-size: 0.9rem;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
+ position: relative;
+ overflow: hidden;
+ border: 2px solid transparent;
+ }
+
+ .htaccess-btn:hover {
+ background: #f0fdf4;
+ color: #065f46;
+ transform: translateY(-2px) scale(1.02);
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
+ text-decoration: none;
+ border-color: white;
+ }
+
+ .htaccess-btn::before {
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 0;
+ height: 0;
+ border-radius: 50%;
+ background: rgba(16, 185, 129, 0.1);
+ transform: translate(-50%, -50%);
+ transition: width 0.6s ease, height 0.6s ease;
+ }
+
+ .htaccess-btn:hover::before {
+ width: 300px;
+ height: 300px;
+ }
+
+ .htaccess-btn span {
+ position: relative;
+ z-index: 1;
+ }
+
+ .htaccess-btn i {
+ font-size: 1rem;
+ position: relative;
+ z-index: 1;
+ }
+
+ .htaccess-close {
+ background: rgba(255, 255, 255, 0.15);
+ border: 1px solid rgba(255, 255, 255, 0.3);
+ color: white;
+ font-size: 1.1rem;
+ cursor: pointer;
+ padding: 10px;
+ border-radius: 8px;
+ transition: all 0.3s ease;
+ backdrop-filter: blur(12px);
+ }
+
+ .htaccess-close:hover {
+ background: rgba(255, 255, 255, 0.25);
+ transform: scale(1.1) rotate(90deg);
+ }
+
+ /* Adjust main content when .htaccess banner is shown */
+ .htaccess-shown #main-content {
+ padding-top: 190px;
+ }
+
+ /* Multiple banners adjustments */
+ .notification-shown.htaccess-shown #main-content {
+ padding-top: 240px;
+ }
+
+ .ai-scanner-shown.htaccess-shown #main-content {
+ padding-top: 270px;
+ }
+
+ .notification-shown.ai-scanner-shown.htaccess-shown #main-content {
+ padding-top: 320px;
+ }
+
+ .notification-shown .htaccess-feature-banner {
+ top: 130px;
+ }
+
+ .ai-scanner-shown .htaccess-feature-banner {
+ top: 160px;
+ }
+
+ .notification-shown.ai-scanner-shown .htaccess-feature-banner {
+ top: 210px;
+ }
/* Mobile responsive styles for AI Scanner banner */
@media (max-width: 768px) {
@@ -816,54 +1001,110 @@
left: 0;
padding: 12px 20px;
}
-
+
.ai-scanner-content {
gap: 1rem;
flex-wrap: wrap;
}
-
+
.ai-scanner-text {
min-width: 200px;
}
-
+
.ai-scanner-main-text {
font-size: 0.9rem;
}
-
+
.ai-scanner-sub-text {
font-size: 0.8rem;
}
-
+
.ai-scanner-btn {
padding: 10px 20px;
font-size: 0.8rem;
}
-
+
.ai-scanner-icon {
padding: 10px;
}
-
+
.ai-scanner-icon i {
font-size: 1.25rem;
}
+
+ /* .htaccess banner mobile styles */
+ .htaccess-feature-banner {
+ left: 0;
+ padding: 14px 20px;
+ }
+
+ .htaccess-content {
+ gap: 1rem;
+ flex-wrap: wrap;
+ }
+
+ .htaccess-text {
+ min-width: 200px;
+ }
+
+ .htaccess-main-text {
+ font-size: 0.95rem;
+ }
+
+ .htaccess-sub-text {
+ font-size: 0.8rem;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.5rem;
+ }
+
+ .htaccess-btn {
+ padding: 12px 22px;
+ font-size: 0.85rem;
+ }
+
+ .htaccess-icon {
+ padding: 12px;
+ }
+
+ .htaccess-icon i {
+ font-size: 1.4rem;
+ }
}
-
+
@media (max-width: 480px) {
.ai-scanner-content {
flex-direction: column;
align-items: stretch;
gap: 12px;
}
-
+
.ai-scanner-actions {
justify-content: center;
}
-
+
.ai-scanner-close {
position: absolute;
top: 8px;
right: 8px;
}
+
+ /* .htaccess banner small mobile styles */
+ .htaccess-content {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 12px;
+ }
+
+ .htaccess-actions {
+ justify-content: center;
+ }
+
+ .htaccess-close {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ }
}
/* Scrollbar */
@@ -1760,7 +2001,33 @@
-
+
+
+
+
+
+
+
+
+ ✨ Revolutionary .htaccess Support Now Live!
+
+ Full .htaccess support
+ PHP configuration now works
+ Zero rule rewrites needed
+
+
+
+
+
+
+
{% block content %}{% endblock %}
@@ -1841,10 +2108,10 @@
// Backup notification banner logic
function checkBackupStatus() {
- // Check if user has dismissed the notification permanently (from server-side context)
- {% if backup_notification_dismissed %}
- return; // Notification already dismissed permanently
- {% endif %}
+ // Check if user has dismissed the notification in this session
+ if (sessionStorage.getItem('backupNotificationDismissed') === 'true') {
+ return;
+ }
// Check if user has backup configured (you'll need to implement this API)
// For now, we'll show it by default unless they have a backup plan
@@ -1871,81 +2138,83 @@
const body = document.body;
banner.classList.remove('show');
body.classList.remove('notification-shown');
-
- // Dismiss permanently via API
- fetch('/base/dismiss_backup_notification', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'X-CSRFToken': getCookie('csrftoken')
- }
- })
- .then(response => response.json())
- .then(data => {
- if (data.status === 1) {
- console.log('Backup notification dismissed permanently');
- } else {
- console.error('Failed to dismiss backup notification:', data.error);
- }
- })
- .catch(error => {
- console.error('Error dismissing backup notification:', error);
- });
+ // Remember dismissal for this session
+ sessionStorage.setItem('backupNotificationDismissed', 'true');
}
// AI Scanner Notification Functions
function checkAIScannerStatus() {
- // Check if user has dismissed the notification permanently (from server-side context)
- {% if ai_scanner_notification_dismissed %}
- return; // Notification already dismissed permanently
- {% endif %}
-
+ // Check if user has dismissed the notification in this session
+ if (sessionStorage.getItem('aiScannerNotificationDismissed') === 'true') {
+ return;
+ }
+
// Check if we're not already on the AI Scanner page
if (!window.location.href.includes('aiscanner')) {
showAIScannerNotification();
}
}
-
+
function showAIScannerNotification() {
const banner = document.getElementById('ai-scanner-notification');
const body = document.body;
banner.classList.add('show');
body.classList.add('ai-scanner-shown');
}
-
+
function dismissAIScannerNotification() {
const banner = document.getElementById('ai-scanner-notification');
const body = document.body;
banner.classList.remove('show');
body.classList.remove('ai-scanner-shown');
-
- // Dismiss permanently via API
- fetch('/base/dismiss_ai_scanner_notification', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'X-CSRFToken': getCookie('csrftoken')
- }
- })
- .then(response => response.json())
- .then(data => {
- if (data.status === 1) {
- console.log('AI scanner notification dismissed permanently');
- } else {
- console.error('Failed to dismiss AI scanner notification:', data.error);
- }
- })
- .catch(error => {
- console.error('Error dismissing AI scanner notification:', error);
- });
+ // Remember dismissal for this session
+ sessionStorage.setItem('aiScannerNotificationDismissed', 'true');
}
-
- // Check both notification statuses when page loads
+
+ // .htaccess Feature Notification Functions
+ function checkHtaccessStatus() {
+ // Check if user has dismissed the notification permanently (localStorage for longer persistence)
+ if (localStorage.getItem('htaccessNotificationDismissed') === 'true') {
+ return;
+ }
+
+ // Check if notification has been shown today
+ const lastShown = localStorage.getItem('htaccessNotificationLastShown');
+ const today = new Date().toDateString();
+
+ if (lastShown === today) {
+ return;
+ }
+
+ // Show the notification
+ showHtaccessNotification();
+ localStorage.setItem('htaccessNotificationLastShown', today);
+ }
+
+ function showHtaccessNotification() {
+ const banner = document.getElementById('htaccess-notification');
+ const body = document.body;
+ banner.classList.add('show');
+ body.classList.add('htaccess-shown');
+ }
+
+ function dismissHtaccessNotification() {
+ const banner = document.getElementById('htaccess-notification');
+ const body = document.body;
+ banner.classList.remove('show');
+ body.classList.remove('htaccess-shown');
+ // Remember dismissal permanently
+ localStorage.setItem('htaccessNotificationDismissed', 'true');
+ }
+
+ // Check all notification statuses when page loads
document.addEventListener('DOMContentLoaded', function() {
checkBackupStatus();
// Show AI Scanner notification with a slight delay for better UX
setTimeout(checkAIScannerStatus, 1000);
-
+ // Show .htaccess notification with additional delay for staggered effect
+ setTimeout(checkHtaccessStatus, 1500);
+
// Set active menu state based on current URL
setActiveMenuState();
});
diff --git a/cyberpanel.sh b/cyberpanel.sh
index c1e4d195e..2e4752201 100644
--- a/cyberpanel.sh
+++ b/cyberpanel.sh
@@ -1986,9 +1986,30 @@ Current_Dir="$(pwd)"
rm -f /usr/local/lsws/cyberpanel-tmp
mkdir /usr/local/lsws/cyberpanel-tmp
cd /usr/local/lsws/cyberpanel-tmp || exit
+
+# Try to download timezonedb, but continue if it fails
wget -O timezonedb.tgz https://cyberpanel.sh/pecl.php.net/get/timezonedb
+if [ ! -f timezonedb.tgz ] || [ ! -s timezonedb.tgz ]; then
+ log_info "WARNING: Failed to download timezonedb, skipping installation"
+ cd "$Current_Dir" || exit
+ rm -rf /usr/local/lsws/cyberpanel-tmp
+ return 0
+fi
+
tar xzvf timezonedb.tgz
-cd timezonedb-* || exit
+if [ ! -d timezonedb-* ]; then
+ log_info "WARNING: Failed to extract timezonedb, skipping installation"
+ cd "$Current_Dir" || exit
+ rm -rf /usr/local/lsws/cyberpanel-tmp
+ return 0
+fi
+
+cd timezonedb-* || {
+ log_info "WARNING: Cannot enter timezonedb directory, skipping installation"
+ cd "$Current_Dir" || exit
+ rm -rf /usr/local/lsws/cyberpanel-tmp
+ return 0
+}
# Install required packages for building PHP extensions
if [[ "$Server_OS" = "Ubuntu" ]] ; then
diff --git a/firewall/static/firewall/firewall.js b/firewall/static/firewall/firewall.js
index e70ca46e8..878d8151c 100644
--- a/firewall/static/firewall/firewall.js
+++ b/firewall/static/firewall/firewall.js
@@ -1471,8 +1471,8 @@ app.controller('modSecRulesPack', function ($scope, $http, $timeout, $window) {
var owaspInstalled = false;
var comodoInstalled = false;
- var counterOWASP = 0;
- var counterComodo = 0;
+ var owaspInitialized = false;
+ var comodoInitialized = false;
$('#owaspInstalled').change(function () {
@@ -1480,15 +1480,13 @@ app.controller('modSecRulesPack', function ($scope, $http, $timeout, $window) {
owaspInstalled = $(this).prop('checked');
$scope.ruleFiles = true;
- if (counterOWASP !== 0) {
+ if (owaspInitialized) {
if (owaspInstalled === true) {
installModSecRulesPack('installOWASP');
} else {
installModSecRulesPack('disableOWASP')
}
}
-
- counterOWASP = counterOWASP + 1;
});
$('#comodoInstalled').change(function () {
@@ -1496,7 +1494,7 @@ app.controller('modSecRulesPack', function ($scope, $http, $timeout, $window) {
$scope.ruleFiles = true;
comodoInstalled = $(this).prop('checked');
- if (counterComodo !== 0) {
+ if (comodoInitialized) {
if (comodoInstalled === true) {
installModSecRulesPack('installComodo');
@@ -1505,8 +1503,6 @@ app.controller('modSecRulesPack', function ($scope, $http, $timeout, $window) {
}
}
- counterComodo = counterComodo + 1;
-
});
@@ -1545,6 +1541,9 @@ app.controller('modSecRulesPack', function ($scope, $http, $timeout, $window) {
$('#owaspInstalled').prop('checked', false);
$scope.owaspDisable = true;
}
+ // Mark as initialized after setting initial state
+ owaspInitialized = true;
+
if (response.data.comodoInstalled === 1) {
$('#comodoInstalled').prop('checked', true);
$scope.comodoDisable = false;
@@ -1552,6 +1551,8 @@ app.controller('modSecRulesPack', function ($scope, $http, $timeout, $window) {
$('#comodoInstalled').prop('checked', false);
$scope.comodoDisable = true;
}
+ // Mark as initialized after setting initial state
+ comodoInitialized = true;
} else {
if (response.data.owaspInstalled === 1) {
diff --git a/install/installCyberPanel.py b/install/installCyberPanel.py
index 582b0bc86..1ff6cb0d6 100644
--- a/install/installCyberPanel.py
+++ b/install/installCyberPanel.py
@@ -270,10 +270,346 @@ class InstallCyberPanel:
# Fallback to known latest version
return "6.3.4"
+ def detectArchitecture(self):
+ """Detect system architecture - custom binaries only for x86_64"""
+ try:
+ import platform
+ arch = platform.machine()
+ return arch == "x86_64"
+ except Exception as msg:
+ logging.InstallLog.writeToFile(str(msg) + " [detectArchitecture]")
+ return False
+
+ def detectBinarySuffix(self):
+ """Detect which binary suffix to use based on OS distribution
+ Returns 'ubuntu' for Ubuntu/Debian systems
+ Returns 'rhel8' for RHEL/AlmaLinux/Rocky 8.x systems
+ Returns 'rhel9' for RHEL/AlmaLinux/Rocky 9.x systems
+ """
+ try:
+ # Check /etc/os-release first for more accurate detection
+ if os.path.exists('/etc/os-release'):
+ with open('/etc/os-release', 'r') as f:
+ os_release = f.read().lower()
+
+ # Check for Ubuntu/Debian FIRST
+ if 'ubuntu' in os_release or 'debian' in os_release:
+ return 'ubuntu'
+
+ # Check for RHEL-based distributions and extract version
+ if any(x in os_release for x in ['almalinux', 'rocky', 'rhel', 'centos stream']):
+ # Extract version number
+ for line in os_release.split('\n'):
+ if 'version_id' in line:
+ version = line.split('=')[1].strip('"').split('.')[0]
+ if version == '9':
+ return 'rhel9'
+ elif version == '8':
+ return 'rhel8'
+ # Default to rhel9 if version extraction fails
+ return 'rhel9'
+
+ # Fallback: Use distro variable
+ # Ubuntu/Debian → ubuntu suffix
+ if self.distro == ubuntu:
+ return 'ubuntu'
+
+ # CentOS 8+/AlmaLinux/Rocky/OpenEuler → rhel9 by default
+ elif self.distro == cent8 or self.distro == openeuler:
+ return 'rhel9'
+
+ # CentOS 7 → ubuntu suffix (uses libcrypt.so.1)
+ elif self.distro == centos:
+ return 'ubuntu'
+
+ # Default to ubuntu for unknown distros
+ else:
+ InstallCyberPanel.stdOut("Unknown OS distribution, defaulting to Ubuntu binaries", 1)
+ return 'ubuntu'
+
+ except Exception as msg:
+ logging.InstallLog.writeToFile(str(msg) + " [detectBinarySuffix]")
+ InstallCyberPanel.stdOut("Error detecting OS, defaulting to Ubuntu binaries", 1)
+ return 'ubuntu'
+
+ def downloadCustomBinary(self, url, destination):
+ """Download custom binary file"""
+ try:
+ InstallCyberPanel.stdOut(f"Downloading {os.path.basename(destination)}...", 1)
+
+ # Use wget for better progress display
+ command = f'wget -q --show-progress {url} -O {destination}'
+ install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
+
+ # Check if file was downloaded successfully by verifying it exists and has reasonable size
+ if os.path.exists(destination):
+ file_size = os.path.getsize(destination)
+ # Verify file size is reasonable (at least 10KB to avoid error pages/empty files)
+ if file_size > 10240: # 10KB
+ if file_size > 1048576: # 1MB
+ InstallCyberPanel.stdOut(f"Downloaded successfully ({file_size / (1024*1024):.2f} MB)", 1)
+ else:
+ InstallCyberPanel.stdOut(f"Downloaded successfully ({file_size / 1024:.2f} KB)", 1)
+ return True
+ else:
+ InstallCyberPanel.stdOut(f"ERROR: Downloaded file too small ({file_size} bytes)", 1)
+ return False
+ else:
+ InstallCyberPanel.stdOut("ERROR: Download failed - file not found", 1)
+ return False
+
+ except Exception as msg:
+ logging.InstallLog.writeToFile(str(msg) + " [downloadCustomBinary]")
+ InstallCyberPanel.stdOut(f"ERROR: {msg}", 1)
+ return False
+
+ def verifyCustomBinary(self, binary_path):
+ """Verify custom binary has correct dependencies and can run"""
+ try:
+ InstallCyberPanel.stdOut("Verifying custom binary compatibility...", 1)
+
+ # Check library dependencies
+ command = f'ldd {binary_path}'
+ result = subprocess.run(command, shell=True, capture_output=True, text=True)
+
+ if result.returncode != 0:
+ InstallCyberPanel.stdOut("ERROR: Failed to check binary dependencies", 1)
+ return False
+
+ # Check for missing libraries
+ if 'not found' in result.stdout:
+ InstallCyberPanel.stdOut("ERROR: Binary has missing library dependencies:", 1)
+ for line in result.stdout.split('\n'):
+ if 'not found' in line:
+ InstallCyberPanel.stdOut(f" {line.strip()}", 1)
+ return False
+
+ # Try to run the binary with -v to check if it can execute
+ command = f'{binary_path} -v'
+ result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=5)
+
+ if result.returncode != 0:
+ InstallCyberPanel.stdOut("ERROR: Binary failed to execute", 1)
+ if result.stderr:
+ InstallCyberPanel.stdOut(f" Error: {result.stderr.strip()}", 1)
+ return False
+
+ InstallCyberPanel.stdOut("Binary verification successful", 1)
+ return True
+
+ except subprocess.TimeoutExpired:
+ InstallCyberPanel.stdOut("ERROR: Binary verification timed out", 1)
+ return False
+ except Exception as msg:
+ logging.InstallLog.writeToFile(str(msg) + " [verifyCustomBinary]")
+ InstallCyberPanel.stdOut(f"ERROR: Verification failed: {msg}", 1)
+ return False
+
+ def rollbackCustomBinary(self, backup_dir, binary_path, module_path):
+ """Rollback to original binary if custom binary fails"""
+ try:
+ InstallCyberPanel.stdOut("Rolling back to original binary...", 1)
+
+ backup_binary = f"{backup_dir}/openlitespeed.backup"
+
+ # Restore original binary if backup exists
+ if os.path.exists(backup_binary):
+ shutil.copy2(backup_binary, binary_path)
+ os.chmod(binary_path, 0o755)
+ InstallCyberPanel.stdOut("Original binary restored successfully", 1)
+ else:
+ InstallCyberPanel.stdOut("WARNING: No backup found, cannot restore", 1)
+
+ # Remove failed custom module
+ if os.path.exists(module_path):
+ os.remove(module_path)
+ InstallCyberPanel.stdOut("Custom module removed", 1)
+
+ InstallCyberPanel.stdOut("Rollback completed", 1)
+ return True
+
+ except Exception as msg:
+ logging.InstallLog.writeToFile(str(msg) + " [rollbackCustomBinary]")
+ InstallCyberPanel.stdOut(f"ERROR: Rollback failed: {msg}", 1)
+ return False
+
+ def installCustomOLSBinaries(self):
+ """Install custom OpenLiteSpeed binaries with PHP config support"""
+ try:
+ InstallCyberPanel.stdOut("Installing Custom OpenLiteSpeed Binaries", 1)
+ InstallCyberPanel.stdOut("=" * 50, 1)
+
+ # Check architecture
+ if not self.detectArchitecture():
+ InstallCyberPanel.stdOut("WARNING: Custom binaries only available for x86_64", 1)
+ InstallCyberPanel.stdOut("Skipping custom binary installation", 1)
+ InstallCyberPanel.stdOut("Standard OLS will be used", 1)
+ return True # Not a failure, just skip
+
+ # Detect OS and select appropriate binary suffix
+ binary_suffix = self.detectBinarySuffix()
+ InstallCyberPanel.stdOut(f"Detected OS type: using '{binary_suffix}' binaries", 1)
+
+ # URLs for custom binaries with OS-specific paths
+ BASE_URL = "https://cyberpanel.net/binaries"
+
+ # Set URLs based on OS type
+ if binary_suffix == 'rhel8':
+ OLS_BINARY_URL = f"{BASE_URL}/rhel8/openlitespeed-phpconfig-x86_64-rhel8"
+ MODULE_URL = f"{BASE_URL}/rhel8/cyberpanel_ols_x86_64_rhel8.so"
+ elif binary_suffix == 'rhel9':
+ OLS_BINARY_URL = f"{BASE_URL}/rhel9/openlitespeed-phpconfig-x86_64-rhel"
+ MODULE_URL = f"{BASE_URL}/rhel9/cyberpanel_ols_x86_64_rhel.so"
+ else: # ubuntu
+ OLS_BINARY_URL = f"{BASE_URL}/ubuntu/openlitespeed-phpconfig-x86_64-ubuntu"
+ MODULE_URL = f"{BASE_URL}/ubuntu/cyberpanel_ols_x86_64_ubuntu.so"
+
+ OLS_BINARY_PATH = "/usr/local/lsws/bin/openlitespeed"
+ MODULE_PATH = "/usr/local/lsws/modules/cyberpanel_ols.so"
+
+ # Create backup
+ from datetime import datetime
+ timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
+ backup_dir = f"/usr/local/lsws/backup-{timestamp}"
+
+ try:
+ os.makedirs(backup_dir, exist_ok=True)
+ if os.path.exists(OLS_BINARY_PATH):
+ shutil.copy2(OLS_BINARY_PATH, f"{backup_dir}/openlitespeed.backup")
+ InstallCyberPanel.stdOut(f"Backup created at: {backup_dir}", 1)
+ except Exception as e:
+ InstallCyberPanel.stdOut(f"WARNING: Could not create backup: {e}", 1)
+
+ # Download binaries to temp location
+ tmp_binary = "/tmp/openlitespeed-custom"
+ tmp_module = "/tmp/cyberpanel_ols.so"
+
+ InstallCyberPanel.stdOut("Downloading custom binaries...", 1)
+
+ # Download OpenLiteSpeed binary
+ if not self.downloadCustomBinary(OLS_BINARY_URL, tmp_binary):
+ InstallCyberPanel.stdOut("ERROR: Failed to download OLS binary", 1)
+ InstallCyberPanel.stdOut("Continuing with standard OLS", 1)
+ return True # Not fatal, continue with standard OLS
+
+ # Download module
+ if not self.downloadCustomBinary(MODULE_URL, tmp_module):
+ InstallCyberPanel.stdOut("ERROR: Failed to download module", 1)
+ InstallCyberPanel.stdOut("Continuing with standard OLS", 1)
+ return True # Not fatal, continue with standard OLS
+
+ # Install OpenLiteSpeed binary
+ InstallCyberPanel.stdOut("Installing custom binaries...", 1)
+
+ try:
+ shutil.move(tmp_binary, OLS_BINARY_PATH)
+ os.chmod(OLS_BINARY_PATH, 0o755)
+ InstallCyberPanel.stdOut("Installed OpenLiteSpeed binary", 1)
+ except Exception as e:
+ InstallCyberPanel.stdOut(f"ERROR: Failed to install binary: {e}", 1)
+ logging.InstallLog.writeToFile(str(e) + " [installCustomOLSBinaries - binary install]")
+ return False
+
+ # Install module
+ try:
+ os.makedirs(os.path.dirname(MODULE_PATH), exist_ok=True)
+ shutil.move(tmp_module, MODULE_PATH)
+ os.chmod(MODULE_PATH, 0o644)
+ InstallCyberPanel.stdOut("Installed CyberPanel module", 1)
+ except Exception as e:
+ InstallCyberPanel.stdOut(f"ERROR: Failed to install module: {e}", 1)
+ logging.InstallLog.writeToFile(str(e) + " [installCustomOLSBinaries - module install]")
+ return False
+
+ # Verify installation files exist
+ if not (os.path.exists(OLS_BINARY_PATH) and os.path.exists(MODULE_PATH)):
+ InstallCyberPanel.stdOut("ERROR: Installation verification failed - files not found", 1)
+ return False
+
+ # Verify binary compatibility
+ if not self.verifyCustomBinary(OLS_BINARY_PATH):
+ InstallCyberPanel.stdOut("ERROR: Custom binary verification failed", 1)
+ InstallCyberPanel.stdOut("This usually means wrong binary type for your OS", 1)
+
+ # Rollback to original binary
+ if os.path.exists(backup_dir):
+ self.rollbackCustomBinary(backup_dir, OLS_BINARY_PATH, MODULE_PATH)
+ InstallCyberPanel.stdOut("Continuing with standard OLS", 1)
+ else:
+ InstallCyberPanel.stdOut("WARNING: Cannot rollback, no backup found", 1)
+
+ return True # Non-fatal, continue with standard OLS
+
+ # Success!
+ InstallCyberPanel.stdOut("=" * 50, 1)
+ InstallCyberPanel.stdOut("Custom Binaries Installed Successfully", 1)
+ InstallCyberPanel.stdOut("Features enabled:", 1)
+ InstallCyberPanel.stdOut(" - Apache-style .htaccess support", 1)
+ InstallCyberPanel.stdOut(" - php_value/php_flag directives", 1)
+ InstallCyberPanel.stdOut(" - Enhanced header control", 1)
+ InstallCyberPanel.stdOut(f"Backup: {backup_dir}", 1)
+ InstallCyberPanel.stdOut("=" * 50, 1)
+ return True
+
+ except Exception as msg:
+ logging.InstallLog.writeToFile(str(msg) + " [installCustomOLSBinaries]")
+ InstallCyberPanel.stdOut(f"ERROR: {msg}", 1)
+ InstallCyberPanel.stdOut("Continuing with standard OLS", 1)
+ return True # Non-fatal error, continue
+
+ def configureCustomModule(self):
+ """Configure CyberPanel module in OpenLiteSpeed config"""
+ try:
+ InstallCyberPanel.stdOut("Configuring CyberPanel module...", 1)
+
+ CONFIG_FILE = "/usr/local/lsws/conf/httpd_config.conf"
+
+ if not os.path.exists(CONFIG_FILE):
+ InstallCyberPanel.stdOut("WARNING: Config file not found", 1)
+ InstallCyberPanel.stdOut("Module will be auto-loaded", 1)
+ return True
+
+ # Check if module is already configured
+ with open(CONFIG_FILE, 'r') as f:
+ content = f.read()
+ if 'cyberpanel_ols' in content:
+ InstallCyberPanel.stdOut("Module already configured", 1)
+ return True
+
+ # Add module configuration
+ module_config = """
+module cyberpanel_ols {
+ ls_enabled 1
+}
+"""
+ # Backup config
+ shutil.copy2(CONFIG_FILE, f"{CONFIG_FILE}.backup")
+
+ # Append module config
+ with open(CONFIG_FILE, 'a') as f:
+ f.write(module_config)
+
+ InstallCyberPanel.stdOut("Module configured successfully", 1)
+ return True
+
+ except Exception as msg:
+ logging.InstallLog.writeToFile(str(msg) + " [configureCustomModule]")
+ InstallCyberPanel.stdOut(f"WARNING: Module configuration failed: {msg}", 1)
+ InstallCyberPanel.stdOut("Module may still work via auto-load", 1)
+ return True # Non-fatal
+
def installLiteSpeed(self):
if self.ent == 0:
+ # Install standard OpenLiteSpeed package
self.install_package('openlitespeed')
+ # Install custom binaries with PHP config support
+ # This replaces the standard binary with enhanced version
+ self.installCustomOLSBinaries()
+
+ # Configure the custom module
+ self.configureCustomModule()
+
else:
try:
try:
diff --git a/plogical/DockerSites.py b/plogical/DockerSites.py
index 4e6c8f12d..820e9fcda 100644
--- a/plogical/DockerSites.py
+++ b/plogical/DockerSites.py
@@ -308,7 +308,9 @@ extprocessor docker{port} {{
logging.writeToFile("Context already exists, skipping...")
return True
- # Add proxy context with proper headers for n8n
+ # Add proxy context with Origin header for n8n
+ # Note: OLS proxy automatically adds X-Forwarded-* headers
+ # Only Origin header needs explicit configuration
proxy_context = f'''
# N8N Proxy Configuration
@@ -319,11 +321,7 @@ context / {{
websocket 1
extraHeaders << 10240: # 10KB
+ if file_size > 1048576: # 1MB
+ Upgrade.stdOut(f"Downloaded successfully ({file_size / (1024*1024):.2f} MB)", 0)
+ else:
+ Upgrade.stdOut(f"Downloaded successfully ({file_size / 1024:.2f} KB)", 0)
+ return True
+ else:
+ Upgrade.stdOut(f"ERROR: Downloaded file too small ({file_size} bytes)", 0)
+ return False
+ else:
+ Upgrade.stdOut("ERROR: Download failed - file not found", 0)
+ return False
+
+ except Exception as msg:
+ Upgrade.stdOut(f"ERROR: {msg} [downloadCustomBinary]", 0)
+ return False
+
+ @staticmethod
+ def verifyCustomBinary(binary_path):
+ """Verify custom binary has correct dependencies and can run"""
+ try:
+ Upgrade.stdOut("Verifying custom binary compatibility...", 0)
+
+ # Check library dependencies
+ command = f'ldd {binary_path}'
+ result = subprocess.run(command, shell=True, capture_output=True, text=True)
+
+ if result.returncode != 0:
+ Upgrade.stdOut("ERROR: Failed to check binary dependencies", 0)
+ return False
+
+ # Check for missing libraries
+ if 'not found' in result.stdout:
+ Upgrade.stdOut("ERROR: Binary has missing library dependencies:", 0)
+ for line in result.stdout.split('\n'):
+ if 'not found' in line:
+ Upgrade.stdOut(f" {line.strip()}", 0)
+ return False
+
+ # Try to run the binary with -v to check if it can execute
+ command = f'{binary_path} -v'
+ result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=5)
+
+ if result.returncode != 0:
+ Upgrade.stdOut("ERROR: Binary failed to execute", 0)
+ if result.stderr:
+ Upgrade.stdOut(f" Error: {result.stderr.strip()}", 0)
+ return False
+
+ Upgrade.stdOut("Binary verification successful", 0)
+ return True
+
+ except subprocess.TimeoutExpired:
+ Upgrade.stdOut("ERROR: Binary verification timed out", 0)
+ return False
+ except Exception as msg:
+ Upgrade.stdOut(f"ERROR: Verification failed: {msg}", 0)
+ return False
+
+ @staticmethod
+ def rollbackCustomBinary(backup_dir, binary_path, module_path):
+ """Rollback to original binary if custom binary fails"""
+ try:
+ Upgrade.stdOut("Rolling back to original binary...", 0)
+
+ backup_binary = f"{backup_dir}/openlitespeed.backup"
+
+ # Restore original binary if backup exists
+ if os.path.exists(backup_binary):
+ shutil.copy2(backup_binary, binary_path)
+ os.chmod(binary_path, 0o755)
+ Upgrade.stdOut("Original binary restored successfully", 0)
+ else:
+ Upgrade.stdOut("WARNING: No backup found, cannot restore", 0)
+
+ # Remove failed custom module
+ if os.path.exists(module_path):
+ os.remove(module_path)
+ Upgrade.stdOut("Custom module removed", 0)
+
+ Upgrade.stdOut("Rollback completed", 0)
+ return True
+
+ except Exception as msg:
+ Upgrade.stdOut(f"ERROR: Rollback failed: {msg}", 0)
+ return False
+
+ @staticmethod
+ def installCustomOLSBinaries():
+ """Install custom OpenLiteSpeed binaries with PHP config support"""
+ try:
+ Upgrade.stdOut("Installing Custom OpenLiteSpeed Binaries", 0)
+ Upgrade.stdOut("=" * 50, 0)
+
+ # Check architecture
+ if not Upgrade.detectArchitecture():
+ Upgrade.stdOut("WARNING: Custom binaries only available for x86_64", 0)
+ Upgrade.stdOut("Skipping custom binary installation", 0)
+ Upgrade.stdOut("Standard OLS will be used", 0)
+ return True # Not a failure, just skip
+
+ # Detect OS and select appropriate binary suffix
+ binary_suffix = Upgrade.detectBinarySuffix()
+ Upgrade.stdOut(f"Detected OS type: using '{binary_suffix}' binaries", 0)
+
+ # URLs for custom binaries with OS-specific paths
+ BASE_URL = "https://cyberpanel.net/binaries"
+
+ # Set URLs based on OS type
+ if binary_suffix == 'rhel8':
+ OLS_BINARY_URL = f"{BASE_URL}/rhel8/openlitespeed-phpconfig-x86_64-rhel8"
+ MODULE_URL = f"{BASE_URL}/rhel8/cyberpanel_ols_x86_64_rhel8.so"
+ elif binary_suffix == 'rhel9':
+ OLS_BINARY_URL = f"{BASE_URL}/rhel9/openlitespeed-phpconfig-x86_64-rhel"
+ MODULE_URL = f"{BASE_URL}/rhel9/cyberpanel_ols_x86_64_rhel.so"
+ else: # ubuntu
+ OLS_BINARY_URL = f"{BASE_URL}/ubuntu/openlitespeed-phpconfig-x86_64-ubuntu"
+ MODULE_URL = f"{BASE_URL}/ubuntu/cyberpanel_ols_x86_64_ubuntu.so"
+
+ OLS_BINARY_PATH = "/usr/local/lsws/bin/openlitespeed"
+ MODULE_PATH = "/usr/local/lsws/modules/cyberpanel_ols.so"
+
+ # Create backup
+ from datetime import datetime
+ timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
+ backup_dir = f"/usr/local/lsws/backup-{timestamp}"
+
+ try:
+ os.makedirs(backup_dir, exist_ok=True)
+ if os.path.exists(OLS_BINARY_PATH):
+ shutil.copy2(OLS_BINARY_PATH, f"{backup_dir}/openlitespeed.backup")
+ Upgrade.stdOut(f"Backup created at: {backup_dir}", 0)
+ except Exception as e:
+ Upgrade.stdOut(f"WARNING: Could not create backup: {e}", 0)
+
+ # Download binaries to temp location
+ tmp_binary = "/tmp/openlitespeed-custom"
+ tmp_module = "/tmp/cyberpanel_ols.so"
+
+ Upgrade.stdOut("Downloading custom binaries...", 0)
+
+ # Download OpenLiteSpeed binary
+ if not Upgrade.downloadCustomBinary(OLS_BINARY_URL, tmp_binary):
+ Upgrade.stdOut("ERROR: Failed to download OLS binary", 0)
+ Upgrade.stdOut("Continuing with standard OLS", 0)
+ return True # Not fatal, continue with standard OLS
+
+ # Download module
+ if not Upgrade.downloadCustomBinary(MODULE_URL, tmp_module):
+ Upgrade.stdOut("ERROR: Failed to download module", 0)
+ Upgrade.stdOut("Continuing with standard OLS", 0)
+ return True # Not fatal, continue with standard OLS
+
+ # Install OpenLiteSpeed binary
+ Upgrade.stdOut("Installing custom binaries...", 0)
+
+ try:
+ shutil.move(tmp_binary, OLS_BINARY_PATH)
+ os.chmod(OLS_BINARY_PATH, 0o755)
+ Upgrade.stdOut("Installed OpenLiteSpeed binary", 0)
+ except Exception as e:
+ Upgrade.stdOut(f"ERROR: Failed to install binary: {e}", 0)
+ return False
+
+ # Install module
+ try:
+ os.makedirs(os.path.dirname(MODULE_PATH), exist_ok=True)
+ shutil.move(tmp_module, MODULE_PATH)
+ os.chmod(MODULE_PATH, 0o644)
+ Upgrade.stdOut("Installed CyberPanel module", 0)
+ except Exception as e:
+ Upgrade.stdOut(f"ERROR: Failed to install module: {e}", 0)
+ return False
+
+ # Verify installation files exist
+ if not (os.path.exists(OLS_BINARY_PATH) and os.path.exists(MODULE_PATH)):
+ Upgrade.stdOut("ERROR: Installation verification failed - files not found", 0)
+ return False
+
+ # Verify binary compatibility
+ if not Upgrade.verifyCustomBinary(OLS_BINARY_PATH):
+ Upgrade.stdOut("ERROR: Custom binary verification failed", 0)
+ Upgrade.stdOut("This usually means wrong binary type for your OS", 0)
+
+ # Rollback to original binary
+ if os.path.exists(backup_dir):
+ Upgrade.rollbackCustomBinary(backup_dir, OLS_BINARY_PATH, MODULE_PATH)
+ Upgrade.stdOut("Continuing with standard OLS", 0)
+ else:
+ Upgrade.stdOut("WARNING: Cannot rollback, no backup found", 0)
+
+ return True # Non-fatal, continue with standard OLS
+
+ # Success!
+ Upgrade.stdOut("=" * 50, 0)
+ Upgrade.stdOut("Custom Binaries Installed Successfully", 0)
+ Upgrade.stdOut("Features enabled:", 0)
+ Upgrade.stdOut(" - Apache-style .htaccess support", 0)
+ Upgrade.stdOut(" - php_value/php_flag directives", 0)
+ Upgrade.stdOut(" - Enhanced header control", 0)
+ Upgrade.stdOut(f"Backup: {backup_dir}", 0)
+ Upgrade.stdOut("=" * 50, 0)
+ return True
+
+ except Exception as msg:
+ Upgrade.stdOut(f"ERROR: {msg} [installCustomOLSBinaries]", 0)
+ Upgrade.stdOut("Continuing with standard OLS", 0)
+ return True # Non-fatal error, continue
+
+ @staticmethod
+ def isCustomOLSBinaryInstalled():
+ """Detect if custom OpenLiteSpeed binary is installed"""
+ try:
+ OLS_BINARY_PATH = "/usr/local/lsws/bin/openlitespeed"
+
+ if not os.path.exists(OLS_BINARY_PATH):
+ return False
+
+ # Check for PHPConfig function signature in binary
+ command = f'strings {OLS_BINARY_PATH}'
+ result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=10)
+
+ if result.returncode == 0:
+ # Look for custom binary markers
+ return 'set_php_config_value' in result.stdout or 'PHPConfig LSIAPI' in result.stdout
+
+ return False
+
+ except Exception as msg:
+ Upgrade.stdOut(f"WARNING: Could not detect OLS binary type: {msg}", 0)
+ return False
+
+ @staticmethod
+ def installCompatibleModSecurity():
+ """Install ModSecurity compatible with custom OpenLiteSpeed binary"""
+ try:
+ Upgrade.stdOut("Installing ModSecurity compatible with custom OpenLiteSpeed binary...", 0)
+
+ MODSEC_PATH = "/usr/local/lsws/modules/mod_security.so"
+
+ # Detect OS and select appropriate ModSecurity binary
+ binary_suffix = Upgrade.detectBinarySuffix()
+ BASE_URL = "https://cyberpanel.net/binaries"
+
+ if binary_suffix == 'rhel8':
+ MODSEC_URL = f"{BASE_URL}/rhel8/mod_security-compatible-rhel8.so"
+ EXPECTED_SHA256 = "8c769dfb42711851ec539e9b6ea649616c14b0e85a53eb18755d200ce29bc442"
+ EXPECTED_MD5 = "b7b9eb20de42b7f8c9c8f4c7a019d6ff"
+ elif binary_suffix == 'rhel9':
+ MODSEC_URL = f"{BASE_URL}/rhel9/mod_security-compatible-rhel.so"
+ EXPECTED_SHA256 = "db580afc431fda40d46bdae2249ac74690d9175ff6d8b1843f2837d86f8d602f"
+ EXPECTED_MD5 = "1efa1e442fe8eedf4705584ac194fc95"
+ else: # ubuntu
+ MODSEC_URL = f"{BASE_URL}/ubuntu/mod_security-compatible-ubuntu.so"
+ EXPECTED_SHA256 = "115971fcd44b74bc7c7b097b9cec33ddcfb0fb07bb9b562ec9f4f0691c388a6b"
+ EXPECTED_MD5 = "c3987c41182355c1290530b6553658db"
+
+ # Download to temp location
+ tmp_modsec = "/tmp/mod_security_custom.so"
+
+ Upgrade.stdOut(f"Downloading compatible ModSecurity for {binary_suffix}...", 0)
+ command = f'wget -q --show-progress {MODSEC_URL} -O {tmp_modsec}'
+ result = subprocess.call(shlex.split(command))
+
+ if result != 0 or not os.path.exists(tmp_modsec):
+ Upgrade.stdOut("ERROR: Failed to download ModSecurity", 0)
+ return False
+
+ # Verify checksum
+ Upgrade.stdOut("Verifying checksum...", 0)
+ result = subprocess.run(f'sha256sum {tmp_modsec}', shell=True, capture_output=True, text=True)
+ actual_sha256 = result.stdout.split()[0]
+
+ if actual_sha256 != EXPECTED_SHA256:
+ Upgrade.stdOut(f"ERROR: Checksum verification failed", 0)
+ Upgrade.stdOut(f" Expected: {EXPECTED_SHA256}", 0)
+ Upgrade.stdOut(f" Got: {actual_sha256}", 0)
+ os.remove(tmp_modsec)
+ return False
+
+ Upgrade.stdOut("Checksum verified successfully", 0)
+
+ # Backup existing ModSecurity if present
+ if os.path.exists(MODSEC_PATH):
+ backup_path = f"{MODSEC_PATH}.backup.{int(time.time())}"
+ shutil.copy2(MODSEC_PATH, backup_path)
+ Upgrade.stdOut(f"Backed up existing ModSecurity to: {backup_path}", 0)
+
+ # Stop OpenLiteSpeed
+ Upgrade.stdOut("Stopping OpenLiteSpeed...", 0)
+ subprocess.run(['/usr/local/lsws/bin/lswsctrl', 'stop'], timeout=30)
+ time.sleep(2)
+
+ # Install compatible ModSecurity
+ os.makedirs(os.path.dirname(MODSEC_PATH), exist_ok=True)
+ shutil.copy2(tmp_modsec, MODSEC_PATH)
+ os.chmod(MODSEC_PATH, 0o755)
+ os.remove(tmp_modsec)
+
+ Upgrade.stdOut("Compatible ModSecurity installed successfully", 0)
+
+ # Start OpenLiteSpeed
+ Upgrade.stdOut("Starting OpenLiteSpeed...", 0)
+ subprocess.run(['/usr/local/lsws/bin/lswsctrl', 'start'], timeout=30)
+
+ Upgrade.stdOut("✓ ModSecurity updated to compatible version", 0)
+ return True
+
+ except subprocess.TimeoutExpired:
+ Upgrade.stdOut("ERROR: Timeout during OpenLiteSpeed restart", 0)
+ return False
+ except Exception as msg:
+ Upgrade.stdOut(f"ERROR: ModSecurity installation failed: {msg}", 0)
+ return False
+
+ @staticmethod
+ def handleModSecurityCompatibility():
+ """Check and update ModSecurity if custom OLS binary is installed"""
+ try:
+ MODSEC_PATH = "/usr/local/lsws/modules/mod_security.so"
+
+ # Check if ModSecurity is installed
+ if not os.path.exists(MODSEC_PATH):
+ Upgrade.stdOut("ModSecurity not installed, skipping compatibility check", 0)
+ return True
+
+ # Check if custom OLS binary is installed
+ if not Upgrade.isCustomOLSBinaryInstalled():
+ Upgrade.stdOut("Stock OLS binary detected, ModSecurity compatibility check not needed", 0)
+ return True
+
+ Upgrade.stdOut("=" * 50, 0)
+ Upgrade.stdOut("Detected ModSecurity with custom OpenLiteSpeed binary", 0)
+ Upgrade.stdOut("Updating to ABI-compatible ModSecurity version...", 0)
+ Upgrade.stdOut("=" * 50, 0)
+
+ # Install compatible version
+ if Upgrade.installCompatibleModSecurity():
+ Upgrade.stdOut("ModSecurity compatibility update completed", 0)
+ return True
+ else:
+ Upgrade.stdOut("WARNING: ModSecurity compatibility update failed", 0)
+ Upgrade.stdOut("Server may experience crashes. Please contact support.", 0)
+ return False
+
+ except Exception as msg:
+ Upgrade.stdOut(f"ERROR in ModSecurity compatibility check: {msg}", 0)
+ return False
+
+ @staticmethod
+ def configureCustomModule():
+ """Configure CyberPanel module in OpenLiteSpeed config"""
+ try:
+ Upgrade.stdOut("Configuring CyberPanel module...", 0)
+
+ CONFIG_FILE = "/usr/local/lsws/conf/httpd_config.conf"
+
+ if not os.path.exists(CONFIG_FILE):
+ Upgrade.stdOut("WARNING: Config file not found", 0)
+ Upgrade.stdOut("Module will be auto-loaded", 0)
+ return True
+
+ # Check if module is already configured
+ with open(CONFIG_FILE, 'r') as f:
+ content = f.read()
+ if 'cyberpanel_ols' in content:
+ Upgrade.stdOut("Module already configured", 0)
+ return True
+
+ # Add module configuration
+ module_config = """
+module cyberpanel_ols {
+ ls_enabled 1
+}
+"""
+ # Backup config
+ shutil.copy2(CONFIG_FILE, f"{CONFIG_FILE}.backup")
+
+ # Append module config
+ with open(CONFIG_FILE, 'a') as f:
+ f.write(module_config)
+
+ Upgrade.stdOut("Module configured successfully", 0)
+ return True
+
+ except Exception as msg:
+ Upgrade.stdOut(f"WARNING: Module configuration failed: {msg}", 0)
+ Upgrade.stdOut("Module may still work via auto-load", 0)
+ return True # Non-fatal
+
@staticmethod
def download_install_phpmyadmin():
try:
@@ -4287,7 +4752,7 @@ pm.max_spare_servers = 3
## Add LSPHP7.4 TO LSWS Ent configs
if not os.path.exists('/usr/local/lsws/bin/openlitespeed'):
-
+ # This is Enterprise LSWS
if os.path.exists('httpd_config.xml'):
os.remove('httpd_config.xml')
@@ -4295,6 +4760,25 @@ pm.max_spare_servers = 3
Upgrade.executioner(command, command, 0)
# os.remove('/usr/local/lsws/conf/httpd_config.xml')
# shutil.copy('httpd_config.xml', '/usr/local/lsws/conf/httpd_config.xml')
+ else:
+ # This is OpenLiteSpeed - install/upgrade custom binaries
+ Upgrade.stdOut("Detected OpenLiteSpeed installation", 0)
+ Upgrade.stdOut("Installing/upgrading custom binaries with .htaccess PHP config support...", 0)
+
+ # Install custom binaries
+ if Upgrade.installCustomOLSBinaries():
+ # Configure the custom module
+ Upgrade.configureCustomModule()
+
+ # Check and update ModSecurity compatibility if needed
+ Upgrade.handleModSecurityCompatibility()
+
+ # Restart OpenLiteSpeed to apply changes
+ Upgrade.stdOut("Restarting OpenLiteSpeed...", 0)
+ command = '/usr/local/lsws/bin/lswsctrl restart'
+ Upgrade.executioner(command, 'Restart OpenLiteSpeed', 0)
+ else:
+ Upgrade.stdOut("Custom binary installation failed, continuing with upgrade...", 0)
Upgrade.updateRepoURL()