mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-10-26 15:56:34 +01:00
Merge pull request #1503 from master3395/v2.5.5-dev
V2.5.5 dev - Add Debian 13 support
This commit is contained in:
32
CyberCP/phpmyadminMiddleware.py
Normal file
32
CyberCP/phpmyadminMiddleware.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
phpMyAdmin Access Control Middleware
|
||||||
|
|
||||||
|
This middleware checks if users are trying to access phpMyAdmin directly
|
||||||
|
without being logged into CyberPanel and redirects them to the login page.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
|
||||||
|
class PhpMyAdminAccessMiddleware:
|
||||||
|
"""
|
||||||
|
Middleware to control phpMyAdmin access and redirect unauthenticated users to login page.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
# Check if the request is for phpMyAdmin
|
||||||
|
if request.path.startswith('/phpmyadmin/'):
|
||||||
|
# Check if user is authenticated (has session)
|
||||||
|
if 'userID' not in request.session:
|
||||||
|
# Redirect to CyberPanel login page
|
||||||
|
login_url = '/base/'
|
||||||
|
return HttpResponseRedirect(login_url)
|
||||||
|
|
||||||
|
response = self.get_response(request)
|
||||||
|
return response
|
||||||
@@ -87,7 +87,8 @@ MIDDLEWARE = [
|
|||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
'CyberCP.secMiddleware.secMiddleware'
|
'CyberCP.secMiddleware.secMiddleware',
|
||||||
|
'CyberCP.phpmyadminMiddleware.PhpMyAdminAccessMiddleware'
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = 'CyberCP.urls'
|
ROOT_URLCONF = 'CyberCP.urls'
|
||||||
@@ -106,6 +107,7 @@ TEMPLATES = [
|
|||||||
'django.contrib.messages.context_processors.messages',
|
'django.contrib.messages.context_processors.messages',
|
||||||
'baseTemplate.context_processors.version_context',
|
'baseTemplate.context_processors.version_context',
|
||||||
'baseTemplate.context_processors.cosmetic_context',
|
'baseTemplate.context_processors.cosmetic_context',
|
||||||
|
'baseTemplate.context_processors.notification_preferences_context',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
26
README.md
26
README.md
@@ -30,6 +30,7 @@ CyberPanel comes with comprehensive documentation and step-by-step guides:
|
|||||||
- 🐳 **[Docker Command Execution](guides/Docker_Command_Execution_Guide.md)** - Execute commands in Docker containers
|
- 🐳 **[Docker Command Execution](guides/Docker_Command_Execution_Guide.md)** - Execute commands in Docker containers
|
||||||
- 🤖 **[AI Scanner Setup](guides/AIScannerDocs.md)** - Configure AI-powered security scanning
|
- 🤖 **[AI Scanner Setup](guides/AIScannerDocs.md)** - Configure AI-powered security scanning
|
||||||
- 📧 **[Mautic Installation](guides/MAUTIC_INSTALLATION_GUIDE.md)** - Email marketing platform setup
|
- 📧 **[Mautic Installation](guides/MAUTIC_INSTALLATION_GUIDE.md)** - Email marketing platform setup
|
||||||
|
- 🎨 **[Custom CSS Guide](guides/CUSTOM_CSS_GUIDE.md)** - Create custom themes for CyberPanel 2.5.5-dev
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -39,9 +40,9 @@ CyberPanel supports a wide range of PHP versions across different operating syst
|
|||||||
|
|
||||||
### ☑️ **Currently Supported PHP Versions**
|
### ☑️ **Currently Supported PHP Versions**
|
||||||
|
|
||||||
- **PHP 8.5** - Latest stable version (EOL: Dec 2028)
|
- **PHP 8.5** - Latest stable version (EOL: Dec 2028) ⭐ **NEW!**
|
||||||
- **PHP 8.4** - Stable version (EOL: Dec 2027)
|
- **PHP 8.4** - Stable version (EOL: Dec 2027)
|
||||||
- **PHP 8.3** - Stable version (EOL: Dec 2027)
|
- **PHP 8.3** - **Default version** - Stable version (EOL: Dec 2027) 🎯
|
||||||
- **PHP 8.2** - Stable version (EOL: Dec 2026)
|
- **PHP 8.2** - Stable version (EOL: Dec 2026)
|
||||||
- **PHP 8.1** - Stable version (EOL: Dec 2025)
|
- **PHP 8.1** - Stable version (EOL: Dec 2025)
|
||||||
- **PHP 8.0** - Legacy support (EOL: Nov 2023)
|
- **PHP 8.0** - Legacy support (EOL: Nov 2023)
|
||||||
@@ -79,6 +80,9 @@ CyberPanel runs on x86_64 architecture and supports the following operating syst
|
|||||||
- **Ubuntu 24.04.3** - Supported until April 2029 ⭐ **NEW!**
|
- **Ubuntu 24.04.3** - Supported until April 2029 ⭐ **NEW!**
|
||||||
- **Ubuntu 22.04** - Supported until April 2027
|
- **Ubuntu 22.04** - Supported until April 2027
|
||||||
- **Ubuntu 20.04** - Supported until April 2025
|
- **Ubuntu 20.04** - Supported until April 2025
|
||||||
|
- **Debian 13** - Supported until 2029 ⭐ **NEW!**
|
||||||
|
- **Debian 12** - Supported until 2027
|
||||||
|
- **Debian 11** - Supported until 2026
|
||||||
- **AlmaLinux 10** - Supported until May 2030 ⭐ **NEW!**
|
- **AlmaLinux 10** - Supported until May 2030 ⭐ **NEW!**
|
||||||
- **AlmaLinux 9** - Supported until May 2032
|
- **AlmaLinux 9** - Supported until May 2032
|
||||||
- **AlmaLinux 8** - Supported until May 2029
|
- **AlmaLinux 8** - Supported until May 2029
|
||||||
@@ -93,7 +97,6 @@ CyberPanel runs on x86_64 architecture and supports the following operating syst
|
|||||||
|
|
||||||
Additional operating systems may be supported through third-party repositories or community efforts:
|
Additional operating systems may be supported through third-party repositories or community efforts:
|
||||||
|
|
||||||
- **Debian** - May work with Ubuntu-compatible packages
|
|
||||||
- **openEuler** - Community-supported with limited testing
|
- **openEuler** - Community-supported with limited testing
|
||||||
- **Other RHEL derivatives** - May work with AlmaLinux/RockyLinux packages
|
- **Other RHEL derivatives** - May work with AlmaLinux/RockyLinux packages
|
||||||
|
|
||||||
@@ -109,7 +112,6 @@ Install CyberPanel easily with the following command:
|
|||||||
sh <(curl https://cyberpanel.net/install.sh || wget -O - https://cyberpanel.net/install.sh)
|
sh <(curl https://cyberpanel.net/install.sh || wget -O - https://cyberpanel.net/install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📊 Upgrading CyberPanel
|
## 📊 Upgrading CyberPanel
|
||||||
@@ -124,17 +126,20 @@ sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgr
|
|||||||
|
|
||||||
## 🆕 Recent Updates & Fixes
|
## 🆕 Recent Updates & Fixes
|
||||||
|
|
||||||
### **Bandwidth Reset Issue Fixed** (January 2025)
|
### **Bandwidth Reset Issue Fixed** (September 2025)
|
||||||
|
|
||||||
- **Issue**: Monthly bandwidth usage was not resetting, causing cumulative values to grow indefinitely
|
- **Issue**: Monthly bandwidth usage was not resetting, causing cumulative values to grow indefinitely
|
||||||
- **Solution**: Implemented automatic monthly bandwidth reset for all websites and child domains
|
- **Solution**: Implemented automatic monthly bandwidth reset for all websites and child domains
|
||||||
- **Affected OS**: All supported operating systems (Ubuntu, AlmaLinux, RockyLinux, RHEL, CloudLinux, CentOS)
|
- **Affected OS**: All supported operating systems (Ubuntu, AlmaLinux, RockyLinux, RHEL, CloudLinux, CentOS)
|
||||||
- **Manual Reset**: Use `/usr/local/CyberCP/scripts/reset_bandwidth.sh` for immediate reset
|
- **Manual Reset**: Use `/usr/local/CyberCP/scripts/reset_bandwidth.sh` for immediate reset
|
||||||
- **Documentation**: See [Bandwidth Reset Fix Guide](to-do/cyberpanel-bandwidth-reset-fix.md)
|
- **Documentation**: See [Bandwidth Reset Fix Guide](to-do/cyberpanel-bandwidth-reset-fix.md)
|
||||||
|
|
||||||
### **New Operating System Support Added** (January 2025)
|
### **New Operating System Support Added** (September 2025)
|
||||||
|
|
||||||
- **Ubuntu 24.04.3**: Full compatibility with latest Ubuntu LTS
|
- **Ubuntu 24.04.3**: Full compatibility with latest Ubuntu LTS
|
||||||
|
- **Debian 13**: Full compatibility with latest Debian stable release
|
||||||
- **AlmaLinux 10**: Full compatibility with latest AlmaLinux release
|
- **AlmaLinux 10**: Full compatibility with latest AlmaLinux release
|
||||||
- **Long-term Support**: Both supported until 2029-2030
|
- **Long-term Support**: All supported until 2029-2030
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -157,15 +162,17 @@ sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgr
|
|||||||
- 🐳 [Docker Command Execution](guides/Docker_Command_Execution_Guide.md) - Execute commands in Docker containers
|
- 🐳 [Docker Command Execution](guides/Docker_Command_Execution_Guide.md) - Execute commands in Docker containers
|
||||||
- 🤖 [AI Scanner Setup](guides/AIScannerDocs.md) - Configure AI-powered security scanning
|
- 🤖 [AI Scanner Setup](guides/AIScannerDocs.md) - Configure AI-powered security scanning
|
||||||
- 📧 [Mautic Installation](guides/MAUTIC_INSTALLATION_GUIDE.md) - Email marketing platform setup
|
- 📧 [Mautic Installation](guides/MAUTIC_INSTALLATION_GUIDE.md) - Email marketing platform setup
|
||||||
|
- 🎨 [Custom CSS Guide](guides/CUSTOM_CSS_GUIDE.md) - Create custom themes for CyberPanel 2.5.5+
|
||||||
- 📚 [All Guides Index](guides/INDEX.md) - Complete documentation hub
|
- 📚 [All Guides Index](guides/INDEX.md) - Complete documentation hub
|
||||||
|
|
||||||
### 🔗 **Direct Guide Links**
|
### 🔗 **Direct Guide Links**
|
||||||
|
|
||||||
| Feature | Guide | Description |
|
| Feature | Guide | Description |
|
||||||
| ----------- | ---------------------------------------------------------- | ------------------------------ |
|
| ------------ | ---------------------------------------------------------- | ---------------------------------- |
|
||||||
| 🐳 Docker | [Command Execution](guides/Docker_Command_Execution_Guide.md) | Execute commands in containers |
|
| 🐳 Docker | [Command Execution](guides/Docker_Command_Execution_Guide.md) | Execute commands in containers |
|
||||||
| 🤖 Security | [AI Scanner](guides/AIScannerDocs.md) | AI-powered security scanning |
|
| 🤖 Security | [AI Scanner](guides/AIScannerDocs.md) | AI-powered security scanning |
|
||||||
| 📧 Email | [Mautic Setup](guides/MAUTIC_INSTALLATION_GUIDE.md) | Email marketing platform |
|
| 📧 Email | [Mautic Setup](guides/MAUTIC_INSTALLATION_GUIDE.md) | Email marketing platform |
|
||||||
|
| 🎨 Design | [Custom CSS Guide](guides/CUSTOM_CSS_GUIDE.md) | Create custom themes for 2.5.5-dev |
|
||||||
| 📊 Bandwidth | [Reset Fix Guide](to-do/cyberpanel-bandwidth-reset-fix.md) | Fix bandwidth reset issues |
|
| 📊 Bandwidth | [Reset Fix Guide](to-do/cyberpanel-bandwidth-reset-fix.md) | Fix bandwidth reset issues |
|
||||||
| 📚 All | [Complete Index](guides/INDEX.md) | Browse all available guides |
|
| 📚 All | [Complete Index](guides/INDEX.md) | Browse all available guides |
|
||||||
|
|
||||||
@@ -176,12 +183,13 @@ sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgr
|
|||||||
### **Common Issues & Solutions**
|
### **Common Issues & Solutions**
|
||||||
|
|
||||||
#### **Bandwidth Not Resetting Monthly**
|
#### **Bandwidth Not Resetting Monthly**
|
||||||
|
|
||||||
- **Issue**: Bandwidth usage shows cumulative values instead of monthly usage
|
- **Issue**: Bandwidth usage shows cumulative values instead of monthly usage
|
||||||
- **Solution**: Run the bandwidth reset script: `/usr/local/CyberCP/scripts/reset_bandwidth.sh`
|
- **Solution**: Run the bandwidth reset script: `/usr/local/CyberCP/scripts/reset_bandwidth.sh`
|
||||||
- **Prevention**: Ensure monthly cron job is running: `0 0 1 * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/postfixSenderPolicy/client.py monthlyCleanup`
|
- **Prevention**: Ensure monthly cron job is running: `0 0 1 * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/postfixSenderPolicy/client.py monthlyCleanup`
|
||||||
|
|
||||||
|
|
||||||
#### **General Support**
|
#### **General Support**
|
||||||
|
|
||||||
- Check logs: `/usr/local/lscp/logs/error.log`
|
- Check logs: `/usr/local/lscp/logs/error.log`
|
||||||
- Verify cron jobs: `crontab -l`
|
- Verify cron jobs: `crontab -l`
|
||||||
- Test manual reset: Use provided scripts in `/usr/local/CyberCP/scripts/`
|
- Test manual reset: Use provided scripts in `/usr/local/CyberCP/scripts/`
|
||||||
|
|||||||
@@ -6,3 +6,6 @@ from django.apps import AppConfig
|
|||||||
|
|
||||||
class BasetemplateConfig(AppConfig):
|
class BasetemplateConfig(AppConfig):
|
||||||
name = 'baseTemplate'
|
name = 'baseTemplate'
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
import baseTemplate.signals
|
||||||
@@ -24,3 +24,29 @@ def cosmetic_context(request):
|
|||||||
return {
|
return {
|
||||||
'cosmetic': cosmetic
|
'cosmetic': cosmetic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def notification_preferences_context(request):
|
||||||
|
"""Add user notification preferences to all templates"""
|
||||||
|
try:
|
||||||
|
if 'userID' in request.session:
|
||||||
|
from .models import UserNotificationPreferences
|
||||||
|
from loginSystem.models import Administrator
|
||||||
|
user = Administrator.objects.get(pk=request.session['userID'])
|
||||||
|
try:
|
||||||
|
preferences = UserNotificationPreferences.objects.get(user=user)
|
||||||
|
return {
|
||||||
|
'backup_notification_dismissed': preferences.backup_notification_dismissed,
|
||||||
|
'ai_scanner_notification_dismissed': preferences.ai_scanner_notification_dismissed
|
||||||
|
}
|
||||||
|
except UserNotificationPreferences.DoesNotExist:
|
||||||
|
return {
|
||||||
|
'backup_notification_dismissed': False,
|
||||||
|
'ai_scanner_notification_dismissed': False
|
||||||
|
}
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return {
|
||||||
|
'backup_notification_dismissed': False,
|
||||||
|
'ai_scanner_notification_dismissed': False
|
||||||
|
}
|
||||||
32
baseTemplate/migrations/0002_usernotificationpreferences.py
Normal file
32
baseTemplate/migrations/0002_usernotificationpreferences.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.29 on 2024-01-01 00:00
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('baseTemplate', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserNotificationPreferences',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('backup_notification_dismissed', models.BooleanField(default=False, help_text='Whether user has dismissed the backup notification')),
|
||||||
|
('ai_scanner_notification_dismissed', models.BooleanField(default=False, help_text='Whether user has dismissed the AI scanner notification')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='notification_preferences', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'User Notification Preferences',
|
||||||
|
'verbose_name_plural': 'User Notification Preferences',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
||||||
@@ -12,3 +13,18 @@ class version(models.Model):
|
|||||||
|
|
||||||
class CyberPanelCosmetic(models.Model):
|
class CyberPanelCosmetic(models.Model):
|
||||||
MainDashboardCSS = models.TextField(default='')
|
MainDashboardCSS = models.TextField(default='')
|
||||||
|
|
||||||
|
class UserNotificationPreferences(models.Model):
|
||||||
|
"""Model to store user notification dismissal preferences"""
|
||||||
|
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='notification_preferences')
|
||||||
|
backup_notification_dismissed = models.BooleanField(default=False, help_text="Whether user has dismissed the backup notification")
|
||||||
|
ai_scanner_notification_dismissed = models.BooleanField(default=False, help_text="Whether user has dismissed the AI scanner notification")
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "User Notification Preferences"
|
||||||
|
verbose_name_plural = "User Notification Preferences"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Notification Preferences for {self.user.username}"
|
||||||
23
baseTemplate/signals.py
Normal file
23
baseTemplate/signals.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from django.db.models.signals import post_save
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from .models import UserNotificationPreferences
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=User)
|
||||||
|
def create_user_notification_preferences(sender, instance, created, **kwargs):
|
||||||
|
"""Create default notification preferences when a new user is created"""
|
||||||
|
if created:
|
||||||
|
UserNotificationPreferences.objects.create(
|
||||||
|
user=instance,
|
||||||
|
backup_notification_dismissed=False,
|
||||||
|
ai_scanner_notification_dismissed=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=User)
|
||||||
|
def save_user_notification_preferences(sender, instance, **kwargs):
|
||||||
|
"""Save notification preferences when user is saved"""
|
||||||
|
if hasattr(instance, 'notification_preferences'):
|
||||||
|
instance.notification_preferences.save()
|
||||||
@@ -1841,10 +1841,10 @@
|
|||||||
|
|
||||||
// Backup notification banner logic
|
// Backup notification banner logic
|
||||||
function checkBackupStatus() {
|
function checkBackupStatus() {
|
||||||
// Check if user has dismissed the notification in this session
|
// Check if user has dismissed the notification permanently (from server-side context)
|
||||||
if (sessionStorage.getItem('backupNotificationDismissed') === 'true') {
|
{% if backup_notification_dismissed %}
|
||||||
return;
|
return; // Notification already dismissed permanently
|
||||||
}
|
{% endif %}
|
||||||
|
|
||||||
// Check if user has backup configured (you'll need to implement this API)
|
// 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
|
// For now, we'll show it by default unless they have a backup plan
|
||||||
@@ -1871,16 +1871,34 @@
|
|||||||
const body = document.body;
|
const body = document.body;
|
||||||
banner.classList.remove('show');
|
banner.classList.remove('show');
|
||||||
body.classList.remove('notification-shown');
|
body.classList.remove('notification-shown');
|
||||||
// Remember dismissal for this session
|
|
||||||
sessionStorage.setItem('backupNotificationDismissed', 'true');
|
// 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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// AI Scanner Notification Functions
|
// AI Scanner Notification Functions
|
||||||
function checkAIScannerStatus() {
|
function checkAIScannerStatus() {
|
||||||
// Check if user has dismissed the notification in this session
|
// Check if user has dismissed the notification permanently (from server-side context)
|
||||||
if (sessionStorage.getItem('aiScannerNotificationDismissed') === 'true') {
|
{% if ai_scanner_notification_dismissed %}
|
||||||
return;
|
return; // Notification already dismissed permanently
|
||||||
}
|
{% endif %}
|
||||||
|
|
||||||
// Check if we're not already on the AI Scanner page
|
// Check if we're not already on the AI Scanner page
|
||||||
if (!window.location.href.includes('aiscanner')) {
|
if (!window.location.href.includes('aiscanner')) {
|
||||||
@@ -1900,8 +1918,26 @@
|
|||||||
const body = document.body;
|
const body = document.body;
|
||||||
banner.classList.remove('show');
|
banner.classList.remove('show');
|
||||||
body.classList.remove('ai-scanner-shown');
|
body.classList.remove('ai-scanner-shown');
|
||||||
// Remember dismissal for this session
|
|
||||||
sessionStorage.setItem('aiScannerNotificationDismissed', 'true');
|
// 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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check both notification statuses when page loads
|
// Check both notification statuses when page loads
|
||||||
|
|||||||
@@ -24,4 +24,7 @@ urlpatterns = [
|
|||||||
re_path(r'^getSSHUserActivity$', views.getSSHUserActivity, name='getSSHUserActivity'),
|
re_path(r'^getSSHUserActivity$', views.getSSHUserActivity, name='getSSHUserActivity'),
|
||||||
re_path(r'^getTopProcesses$', views.getTopProcesses, name='getTopProcesses'),
|
re_path(r'^getTopProcesses$', views.getTopProcesses, name='getTopProcesses'),
|
||||||
re_path(r'^analyzeSSHSecurity$', views.analyzeSSHSecurity, name='analyzeSSHSecurity'),
|
re_path(r'^analyzeSSHSecurity$', views.analyzeSSHSecurity, name='analyzeSSHSecurity'),
|
||||||
|
re_path(r'^dismiss_backup_notification$', views.dismiss_backup_notification, name='dismiss_backup_notification'),
|
||||||
|
re_path(r'^dismiss_ai_scanner_notification$', views.dismiss_ai_scanner_notification, name='dismiss_ai_scanner_notification'),
|
||||||
|
re_path(r'^get_notification_preferences$', views.get_notification_preferences, name='get_notification_preferences'),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from django.http import HttpResponse
|
|||||||
from plogical.getSystemInformation import SystemInformation
|
from plogical.getSystemInformation import SystemInformation
|
||||||
import json
|
import json
|
||||||
from loginSystem.views import loadLoginPage
|
from loginSystem.views import loadLoginPage
|
||||||
from .models import version
|
from .models import version, UserNotificationPreferences
|
||||||
import requests
|
import requests
|
||||||
import subprocess
|
import subprocess
|
||||||
import shlex
|
import shlex
|
||||||
@@ -1305,3 +1305,88 @@ def getTopProcesses(request):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return HttpResponse(json.dumps({'error': str(e)}), content_type='application/json', status=500)
|
return HttpResponse(json.dumps({'error': str(e)}), content_type='application/json', status=500)
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
@require_POST
|
||||||
|
def dismiss_backup_notification(request):
|
||||||
|
"""API endpoint to permanently dismiss the backup notification for the current user"""
|
||||||
|
try:
|
||||||
|
user_id = request.session.get('userID')
|
||||||
|
if not user_id:
|
||||||
|
return HttpResponse(json.dumps({'status': 0, 'error': 'Not logged in'}), content_type='application/json', status=403)
|
||||||
|
|
||||||
|
# Get or create user notification preferences
|
||||||
|
user = Administrator.objects.get(pk=user_id)
|
||||||
|
preferences, created = UserNotificationPreferences.objects.get_or_create(
|
||||||
|
user=user,
|
||||||
|
defaults={
|
||||||
|
'backup_notification_dismissed': False,
|
||||||
|
'ai_scanner_notification_dismissed': False
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mark backup notification as dismissed
|
||||||
|
preferences.backup_notification_dismissed = True
|
||||||
|
preferences.save()
|
||||||
|
|
||||||
|
return HttpResponse(json.dumps({'status': 1, 'message': 'Backup notification dismissed permanently'}), content_type='application/json')
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return HttpResponse(json.dumps({'status': 0, 'error': str(e)}), content_type='application/json', status=500)
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
@require_POST
|
||||||
|
def dismiss_ai_scanner_notification(request):
|
||||||
|
"""API endpoint to permanently dismiss the AI scanner notification for the current user"""
|
||||||
|
try:
|
||||||
|
user_id = request.session.get('userID')
|
||||||
|
if not user_id:
|
||||||
|
return HttpResponse(json.dumps({'status': 0, 'error': 'Not logged in'}), content_type='application/json', status=403)
|
||||||
|
|
||||||
|
# Get or create user notification preferences
|
||||||
|
user = Administrator.objects.get(pk=user_id)
|
||||||
|
preferences, created = UserNotificationPreferences.objects.get_or_create(
|
||||||
|
user=user,
|
||||||
|
defaults={
|
||||||
|
'backup_notification_dismissed': False,
|
||||||
|
'ai_scanner_notification_dismissed': False
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mark AI scanner notification as dismissed
|
||||||
|
preferences.ai_scanner_notification_dismissed = True
|
||||||
|
preferences.save()
|
||||||
|
|
||||||
|
return HttpResponse(json.dumps({'status': 1, 'message': 'AI scanner notification dismissed permanently'}), content_type='application/json')
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return HttpResponse(json.dumps({'status': 0, 'error': str(e)}), content_type='application/json', status=500)
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
@require_GET
|
||||||
|
def get_notification_preferences(request):
|
||||||
|
"""API endpoint to get current user's notification preferences"""
|
||||||
|
try:
|
||||||
|
user_id = request.session.get('userID')
|
||||||
|
if not user_id:
|
||||||
|
return HttpResponse(json.dumps({'status': 0, 'error': 'Not logged in'}), content_type='application/json', status=403)
|
||||||
|
|
||||||
|
# Get user notification preferences
|
||||||
|
user = Administrator.objects.get(pk=user_id)
|
||||||
|
try:
|
||||||
|
preferences = UserNotificationPreferences.objects.get(user=user)
|
||||||
|
return HttpResponse(json.dumps({
|
||||||
|
'status': 1,
|
||||||
|
'backup_notification_dismissed': preferences.backup_notification_dismissed,
|
||||||
|
'ai_scanner_notification_dismissed': preferences.ai_scanner_notification_dismissed
|
||||||
|
}), content_type='application/json')
|
||||||
|
except UserNotificationPreferences.DoesNotExist:
|
||||||
|
# Return default values if preferences don't exist yet
|
||||||
|
return HttpResponse(json.dumps({
|
||||||
|
'status': 1,
|
||||||
|
'backup_notification_dismissed': False,
|
||||||
|
'ai_scanner_notification_dismissed': False
|
||||||
|
}), content_type='application/json')
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return HttpResponse(json.dumps({'status': 0, 'error': str(e)}), content_type='application/json', status=500)
|
||||||
|
|||||||
@@ -549,12 +549,14 @@ elif grep -q -E "Rocky Linux" /etc/os-release ; then
|
|||||||
Server_OS="RockyLinux"
|
Server_OS="RockyLinux"
|
||||||
elif grep -q -E "Ubuntu 18.04|Ubuntu 20.04|Ubuntu 20.10|Ubuntu 22.04|Ubuntu 24.04" /etc/os-release ; then
|
elif grep -q -E "Ubuntu 18.04|Ubuntu 20.04|Ubuntu 20.10|Ubuntu 22.04|Ubuntu 24.04" /etc/os-release ; then
|
||||||
Server_OS="Ubuntu"
|
Server_OS="Ubuntu"
|
||||||
|
elif grep -q -E "Debian GNU/Linux 11|Debian GNU/Linux 12|Debian GNU/Linux 13" /etc/os-release ; then
|
||||||
|
Server_OS="Debian"
|
||||||
elif grep -q -E "openEuler 20.03|openEuler 22.03" /etc/os-release ; then
|
elif grep -q -E "openEuler 20.03|openEuler 22.03" /etc/os-release ; then
|
||||||
Server_OS="openEuler"
|
Server_OS="openEuler"
|
||||||
else
|
else
|
||||||
echo -e "Unable to detect your system..."
|
echo -e "Unable to detect your system..."
|
||||||
echo -e "\nCyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, Ubuntu 24.04, Ubuntu 24.04.3, CentOS 7, CentOS 8, CentOS 9, RHEL 8, RHEL 9, AlmaLinux 8, AlmaLinux 9, AlmaLinux 10, RockyLinux 8, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03...\n"
|
echo -e "\nCyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, Ubuntu 24.04, Ubuntu 24.04.3, Debian 11, Debian 12, Debian 13, CentOS 7, CentOS 8, CentOS 9, RHEL 8, RHEL 9, AlmaLinux 8, AlmaLinux 9, AlmaLinux 10, RockyLinux 8, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03...\n"
|
||||||
Debug_Log2 "CyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, Ubuntu 24.04, Ubuntu 24.04.3, CentOS 7, CentOS 8, CentOS 9, RHEL 8, RHEL 9, AlmaLinux 8, AlmaLinux 9, AlmaLinux 10, RockyLinux 8, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03... [404]"
|
Debug_Log2 "CyberPanel is supported on x86_64 based Ubuntu 18.04, Ubuntu 20.04, Ubuntu 20.10, Ubuntu 22.04, Ubuntu 24.04, Ubuntu 24.04.3, Debian 11, Debian 12, Debian 13, CentOS 7, CentOS 8, CentOS 9, RHEL 8, RHEL 9, AlmaLinux 8, AlmaLinux 9, AlmaLinux 10, RockyLinux 8, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03... [404]"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -568,6 +570,9 @@ if [[ $Server_OS = "CloudLinux" ]] || [[ "$Server_OS" = "AlmaLinux" ]] || [[ "$S
|
|||||||
Server_OS="CentOS"
|
Server_OS="CentOS"
|
||||||
#CloudLinux gives version id like 7.8, 7.9, so cut it to show first number only
|
#CloudLinux gives version id like 7.8, 7.9, so cut it to show first number only
|
||||||
#treat CloudLinux, Rocky and Alma as CentOS
|
#treat CloudLinux, Rocky and Alma as CentOS
|
||||||
|
elif [[ "$Server_OS" = "Debian" ]] ; then
|
||||||
|
Server_OS="Ubuntu"
|
||||||
|
#Treat Debian as Ubuntu for package management (both use apt-get)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$Debug" = "On" ]] ; then
|
if [[ "$Debug" = "On" ]] ; then
|
||||||
@@ -2278,7 +2283,7 @@ echo "echo \$@ > /etc/cyberpanel/adminPass" >> /usr/bin/adminPass
|
|||||||
chmod 700 /usr/bin/adminPass
|
chmod 700 /usr/bin/adminPass
|
||||||
|
|
||||||
rm -f /usr/bin/php
|
rm -f /usr/bin/php
|
||||||
ln -s /usr/local/lsws/lsphp80/bin/php /usr/bin/php
|
ln -s /usr/local/lsws/lsphp83/bin/php /usr/bin/php
|
||||||
|
|
||||||
if [[ "$Server_OS" = "CentOS" ]] ; then
|
if [[ "$Server_OS" = "CentOS" ]] ; then
|
||||||
#all centos 7/8 post change goes here
|
#all centos 7/8 post change goes here
|
||||||
|
|||||||
54
deploy_phpmyadmin_redirect.sh
Normal file
54
deploy_phpmyadmin_redirect.sh
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# CyberPanel phpMyAdmin Access Control Deployment Script
|
||||||
|
# This script implements redirect functionality for unauthenticated phpMyAdmin access
|
||||||
|
|
||||||
|
echo "=== CyberPanel phpMyAdmin Access Control Deployment ==="
|
||||||
|
|
||||||
|
# Check if running as root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
echo "Please run this script as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup original phpMyAdmin index.php if it exists
|
||||||
|
if [ -f "/usr/local/CyberCP/public/phpmyadmin/index.php" ]; then
|
||||||
|
echo "Backing up original phpMyAdmin index.php..."
|
||||||
|
cp /usr/local/CyberCP/public/phpmyadmin/index.php /usr/local/CyberCP/public/phpmyadmin/index.php.backup.$(date +%Y%m%d_%H%M%S)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Deploy the redirect index.php
|
||||||
|
echo "Deploying phpMyAdmin access control..."
|
||||||
|
cp /usr/local/CyberCP/phpmyadmin_index_redirect.php /usr/local/CyberCP/public/phpmyadmin/index.php
|
||||||
|
|
||||||
|
# Deploy .htaccess for additional protection
|
||||||
|
echo "Deploying .htaccess protection..."
|
||||||
|
cp /usr/local/CyberCP/phpmyadmin_htaccess /usr/local/CyberCP/public/phpmyadmin/.htaccess
|
||||||
|
|
||||||
|
# Set proper permissions
|
||||||
|
echo "Setting permissions..."
|
||||||
|
chown lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin/index.php
|
||||||
|
chmod 644 /usr/local/CyberCP/public/phpmyadmin/index.php
|
||||||
|
chown lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin/.htaccess
|
||||||
|
chmod 644 /usr/local/CyberCP/public/phpmyadmin/.htaccess
|
||||||
|
|
||||||
|
# Restart LiteSpeed to ensure changes take effect
|
||||||
|
echo "Restarting LiteSpeed..."
|
||||||
|
systemctl restart lscpd
|
||||||
|
|
||||||
|
echo "=== Deployment Complete ==="
|
||||||
|
echo ""
|
||||||
|
echo "phpMyAdmin access control has been deployed successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "What this does:"
|
||||||
|
echo "- Users trying to access phpMyAdmin directly without being logged into CyberPanel"
|
||||||
|
echo " will now be redirected to the CyberPanel login page (/base/)"
|
||||||
|
echo "- Authenticated users will continue to access phpMyAdmin normally"
|
||||||
|
echo ""
|
||||||
|
echo "To revert changes, restore the backup:"
|
||||||
|
echo "cp /usr/local/CyberCP/public/phpmyadmin/index.php.backup.* /usr/local/CyberCP/public/phpmyadmin/index.php"
|
||||||
|
echo ""
|
||||||
|
echo "Test the implementation by:"
|
||||||
|
echo "1. Opening an incognito/private browser window"
|
||||||
|
echo "2. Going to https://your-server:2087/phpmyadmin/"
|
||||||
|
echo "3. You should be redirected to the CyberPanel login page"
|
||||||
@@ -323,17 +323,26 @@ class ContainerManager(multi.Thread):
|
|||||||
|
|
||||||
if 'ExposedPorts' in inspectImage['Config']:
|
if 'ExposedPorts' in inspectImage['Config']:
|
||||||
for item in inspectImage['Config']['ExposedPorts']:
|
for item in inspectImage['Config']['ExposedPorts']:
|
||||||
|
# Check if port data exists and is valid
|
||||||
|
if item in data and data[item]:
|
||||||
|
try:
|
||||||
|
port_num = int(data[item])
|
||||||
# Do not allow priviledged port numbers
|
# Do not allow priviledged port numbers
|
||||||
if int(data[item]) < 1024 or int(data[item]) > 65535:
|
if port_num < 1024 or port_num > 65535:
|
||||||
data_ret = {'createContainerStatus': 0, 'error_message': "Choose port between 1024 and 65535"}
|
data_ret = {'createContainerStatus': 0, 'error_message': "Choose port between 1024 and 65535"}
|
||||||
json_data = json.dumps(data_ret)
|
json_data = json.dumps(data_ret)
|
||||||
return HttpResponse(json_data)
|
return HttpResponse(json_data)
|
||||||
portConfig[item] = data[item]
|
portConfig[item] = data[item]
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
data_ret = {'createContainerStatus': 0, 'error_message': f"Invalid port number: {data[item]}"}
|
||||||
|
json_data = json.dumps(data_ret)
|
||||||
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
volumes = {}
|
volumes = {}
|
||||||
|
if volList:
|
||||||
for index, volume in volList.items():
|
for index, volume in volList.items():
|
||||||
volumes[volume['src']] = {'bind': volume['dest'],
|
if isinstance(volume, dict) and 'src' in volume and 'dest' in volume:
|
||||||
'mode': 'rw'}
|
volumes[volume['src']] = {'bind': volume['dest'], 'mode': 'rw'}
|
||||||
|
|
||||||
## Create Configurations
|
## Create Configurations
|
||||||
admin = Administrator.objects.get(userName=dockerOwner)
|
admin = Administrator.objects.get(userName=dockerOwner)
|
||||||
@@ -351,10 +360,15 @@ class ContainerManager(multi.Thread):
|
|||||||
try:
|
try:
|
||||||
container = client.containers.create(**containerArgs)
|
container = client.containers.create(**containerArgs)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
if "port is already allocated" in err: # We need to delete container if port is not available
|
# Check if it's a port allocation error by converting to string first
|
||||||
|
error_message = str(err)
|
||||||
|
if "port is already allocated" in error_message: # We need to delete container if port is not available
|
||||||
print("Deleting container")
|
print("Deleting container")
|
||||||
|
try:
|
||||||
container.remove(force=True)
|
container.remove(force=True)
|
||||||
data_ret = {'createContainerStatus': 0, 'error_message': str(err)}
|
except:
|
||||||
|
pass # Container might not exist yet
|
||||||
|
data_ret = {'createContainerStatus': 0, 'error_message': error_message}
|
||||||
json_data = json.dumps(data_ret)
|
json_data = json.dumps(data_ret)
|
||||||
return HttpResponse(json_data)
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
@@ -376,7 +390,9 @@ class ContainerManager(multi.Thread):
|
|||||||
|
|
||||||
|
|
||||||
except Exception as msg:
|
except Exception as msg:
|
||||||
data_ret = {'createContainerStatus': 0, 'error_message': str(msg)}
|
# Ensure error message is properly converted to string
|
||||||
|
error_message = str(msg) if msg else "Unknown error occurred"
|
||||||
|
data_ret = {'createContainerStatus': 0, 'error_message': error_message}
|
||||||
json_data = json.dumps(data_ret)
|
json_data = json.dumps(data_ret)
|
||||||
return HttpResponse(json_data)
|
return HttpResponse(json_data)
|
||||||
|
|
||||||
|
|||||||
@@ -273,6 +273,54 @@ app.controller('runContainer', function ($scope, $http) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Helper function to generate Docker Compose YAML
|
||||||
|
function generateDockerComposeYml(containerInfo) {
|
||||||
|
var yml = 'version: \'3.8\'\n\n';
|
||||||
|
yml += 'services:\n';
|
||||||
|
yml += ' ' + containerInfo.name + ':\n';
|
||||||
|
yml += ' image: ' + containerInfo.image + '\n';
|
||||||
|
yml += ' container_name: ' + containerInfo.name + '\n';
|
||||||
|
|
||||||
|
// Add ports
|
||||||
|
var ports = Object.keys(containerInfo.ports);
|
||||||
|
if (ports.length > 0) {
|
||||||
|
yml += ' ports:\n';
|
||||||
|
for (var i = 0; i < ports.length; i++) {
|
||||||
|
var port = ports[i];
|
||||||
|
if (containerInfo.ports[port]) {
|
||||||
|
yml += ' - "' + containerInfo.ports[port] + ':' + port + '"\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add volumes
|
||||||
|
var volumes = Object.keys(containerInfo.volumes);
|
||||||
|
if (volumes.length > 0) {
|
||||||
|
yml += ' volumes:\n';
|
||||||
|
for (var i = 0; i < volumes.length; i++) {
|
||||||
|
var volume = volumes[i];
|
||||||
|
if (containerInfo.volumes[volume]) {
|
||||||
|
yml += ' - ' + containerInfo.volumes[volume] + ':' + volume + '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add environment variables
|
||||||
|
var envVars = Object.keys(containerInfo.environment);
|
||||||
|
if (envVars.length > 0) {
|
||||||
|
yml += ' environment:\n';
|
||||||
|
for (var i = 0; i < envVars.length; i++) {
|
||||||
|
var envVar = envVars[i];
|
||||||
|
yml += ' - ' + envVar + '=' + containerInfo.environment[envVar] + '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add restart policy
|
||||||
|
yml += ' restart: unless-stopped\n';
|
||||||
|
|
||||||
|
return yml;
|
||||||
|
}
|
||||||
|
|
||||||
// Docker Compose Functions for runContainer
|
// Docker Compose Functions for runContainer
|
||||||
$scope.generateDockerCompose = function() {
|
$scope.generateDockerCompose = function() {
|
||||||
// Get container information from form
|
// Get container information from form
|
||||||
@@ -1434,101 +1482,6 @@ app.controller('viewContainer', function ($scope, $http, $interval, $timeout) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper function to generate Docker Compose YAML
|
|
||||||
function generateDockerComposeYml(containerInfo) {
|
|
||||||
var yml = 'version: \'3.8\'\n\n';
|
|
||||||
yml += 'services:\n';
|
|
||||||
yml += ' ' + containerInfo.name + ':\n';
|
|
||||||
yml += ' image: ' + containerInfo.image + '\n';
|
|
||||||
yml += ' container_name: ' + containerInfo.name + '\n';
|
|
||||||
|
|
||||||
// Add ports
|
|
||||||
var ports = Object.keys(containerInfo.ports);
|
|
||||||
if (ports.length > 0) {
|
|
||||||
yml += ' ports:\n';
|
|
||||||
for (var i = 0; i < ports.length; i++) {
|
|
||||||
var port = ports[i];
|
|
||||||
if (containerInfo.ports[port]) {
|
|
||||||
yml += ' - "' + containerInfo.ports[port] + ':' + port + '"\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add volumes
|
|
||||||
var volumes = Object.keys(containerInfo.volumes);
|
|
||||||
if (volumes.length > 0) {
|
|
||||||
yml += ' volumes:\n';
|
|
||||||
for (var i = 0; i < volumes.length; i++) {
|
|
||||||
var volume = volumes[i];
|
|
||||||
if (containerInfo.volumes[volume]) {
|
|
||||||
yml += ' - ' + containerInfo.volumes[volume] + ':' + volume + '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add environment variables
|
|
||||||
var envVars = Object.keys(containerInfo.environment);
|
|
||||||
if (envVars.length > 0) {
|
|
||||||
yml += ' environment:\n';
|
|
||||||
for (var i = 0; i < envVars.length; i++) {
|
|
||||||
var envVar = envVars[i];
|
|
||||||
yml += ' - ' + envVar + '=' + containerInfo.environment[envVar] + '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add restart policy
|
|
||||||
yml += ' restart: unless-stopped\n';
|
|
||||||
|
|
||||||
return yml;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to generate Docker Compose YAML (for runContainer)
|
|
||||||
function generateDockerComposeYml(containerInfo) {
|
|
||||||
var yml = 'version: \'3.8\'\n\n';
|
|
||||||
yml += 'services:\n';
|
|
||||||
yml += ' ' + containerInfo.name + ':\n';
|
|
||||||
yml += ' image: ' + containerInfo.image + '\n';
|
|
||||||
yml += ' container_name: ' + containerInfo.name + '\n';
|
|
||||||
|
|
||||||
// Add ports
|
|
||||||
var ports = Object.keys(containerInfo.ports);
|
|
||||||
if (ports.length > 0) {
|
|
||||||
yml += ' ports:\n';
|
|
||||||
for (var i = 0; i < ports.length; i++) {
|
|
||||||
var port = ports[i];
|
|
||||||
if (containerInfo.ports[port]) {
|
|
||||||
yml += ' - "' + containerInfo.ports[port] + ':' + port + '"\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add volumes
|
|
||||||
var volumes = Object.keys(containerInfo.volumes);
|
|
||||||
if (volumes.length > 0) {
|
|
||||||
yml += ' volumes:\n';
|
|
||||||
for (var i = 0; i < volumes.length; i++) {
|
|
||||||
var volume = volumes[i];
|
|
||||||
if (containerInfo.volumes[volume]) {
|
|
||||||
yml += ' - ' + containerInfo.volumes[volume] + ':' + volume + '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add environment variables
|
|
||||||
var envVars = Object.keys(containerInfo.environment);
|
|
||||||
if (envVars.length > 0) {
|
|
||||||
yml += ' environment:\n';
|
|
||||||
for (var i = 0; i < envVars.length; i++) {
|
|
||||||
var envVar = envVars[i];
|
|
||||||
yml += ' - ' + envVar + '=' + containerInfo.environment[envVar] + '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add restart policy
|
|
||||||
yml += ' restart: unless-stopped\n';
|
|
||||||
|
|
||||||
return yml;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.showTop = function () {
|
$scope.showTop = function () {
|
||||||
$scope.topHead = [];
|
$scope.topHead = [];
|
||||||
|
|||||||
14
faq.sh
14
faq.sh
@@ -28,7 +28,7 @@ ${BLUE}------------------------------------------------------------${NC}
|
|||||||
|
|
||||||
${PURPLE}3.${NC} How to access LiteSpeed webadmin console ?
|
${PURPLE}3.${NC} How to access LiteSpeed webadmin console ?
|
||||||
|
|
||||||
Please check this post: ${GREEN}https://forums.cyberpanel.net/discussion/87/tutorial-how-to-setup-and-login-to-openlitespeed-webadmin-console/p1${NC}
|
Please check this post: ${GREEN}https://community.cyberpanel.net/c/support/55${NC}
|
||||||
|
|
||||||
${BLUE}------------------------------------------------------------${NC}
|
${BLUE}------------------------------------------------------------${NC}
|
||||||
|
|
||||||
@@ -52,9 +52,9 @@ ${BLUE}------------------------------------------------------------${NC}
|
|||||||
|
|
||||||
${PURPLE}6.${NC} How to raise upload limit for cyberpanel's phpMyAdmin and File Manager?
|
${PURPLE}6.${NC} How to raise upload limit for cyberpanel's phpMyAdmin and File Manager?
|
||||||
|
|
||||||
edit file ${RED}/usr/local/lsws/lsphp73/etc/php.ini${NC} for CentOS or openEuler
|
edit file ${RED}/usr/local/lsws/lsphp83/etc/php.ini${NC} for CentOS or openEuler
|
||||||
|
|
||||||
${RED}/usr/local/lsws/lsphp73/etc/php/7.3/litespeed/php.ini${NC} for Ubbuntu
|
${RED}/usr/local/lsws/lsphp83/etc/php/8.3/litespeed/php.ini${NC} for Ubuntu
|
||||||
|
|
||||||
find 2 configurations:
|
find 2 configurations:
|
||||||
|
|
||||||
@@ -66,9 +66,9 @@ ${BLUE}------------------------------------------------------------${NC}
|
|||||||
|
|
||||||
${PURPLE}7.${NC} How to add more IPs to my website(s) ?
|
${PURPLE}7.${NC} How to add more IPs to my website(s) ?
|
||||||
|
|
||||||
For OpenLiteSpeed, please check this post: ${GREEN}https://forums.cyberpanel.net/discussion/126/tutorial-how-to-add-2nd-ip-for-websites/p1${NC}
|
For OpenLiteSpeed, please check this post: ${GREEN}https://community.cyberpanel.net/c/support/55${NC}
|
||||||
|
|
||||||
For LiteSpeed Enterprise, please check this post: ${GREEN}https://forums.cyberpanel.net/discussion/3745/tutorial-how-to-add-2nd-ip-for-litespeed-enterprise/p1${NC}
|
For LiteSpeed Enterprise, please check this post: ${GREEN}https://community.cyberpanel.net/c/support/55${NC}
|
||||||
|
|
||||||
${BLUE}------------------------------------------------------------${NC}
|
${BLUE}------------------------------------------------------------${NC}
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ ${BLUE}------------------------------------------------------------${NC}
|
|||||||
|
|
||||||
${PURPLE}9.${NC} How to enable Auto-Index for my site ?
|
${PURPLE}9.${NC} How to enable Auto-Index for my site ?
|
||||||
|
|
||||||
Please check this post ${GREEN}https://forums.cyberpanel.net/discussion/3850/tutorial-how-to-enable-auto-index-on-openlitespeed-and-litespeed-enterprise${NC}
|
Please check this post ${GREEN}https://community.cyberpanel.net/c/support/55${NC}
|
||||||
|
|
||||||
${BLUE}------------------------------------------------------------${NC}
|
${BLUE}------------------------------------------------------------${NC}
|
||||||
|
|
||||||
@@ -111,5 +111,5 @@ ${BLUE}------------------------------------------------------------${NC}
|
|||||||
|
|
||||||
${PURPLE}13.${NC} How to enable PHP error log ?
|
${PURPLE}13.${NC} How to enable PHP error log ?
|
||||||
|
|
||||||
Please check this post ${GREEN}https://forums.cyberpanel.net/discussion/3977/tutorial-how-to-enable-php-error-log/p1${NC}
|
Please check this post ${GREEN}https://community.cyberpanel.net/c/support/55${NC}
|
||||||
"
|
"
|
||||||
@@ -60,6 +60,30 @@ class FirewallManager:
|
|||||||
|
|
||||||
rules = FirewallRules.objects.all()
|
rules = FirewallRules.objects.all()
|
||||||
|
|
||||||
|
# Ensure CyberPanel port 7080 rule exists in database for visibility
|
||||||
|
cyberpanel_rule_exists = False
|
||||||
|
for rule in rules:
|
||||||
|
if rule.port == '7080':
|
||||||
|
cyberpanel_rule_exists = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not cyberpanel_rule_exists:
|
||||||
|
# Create database entry for port 7080 (already enabled in system firewall)
|
||||||
|
try:
|
||||||
|
cyberpanel_rule = FirewallRules(
|
||||||
|
name="CyberPanel Admin",
|
||||||
|
proto="tcp",
|
||||||
|
port="7080",
|
||||||
|
ipAddress="0.0.0.0/0"
|
||||||
|
)
|
||||||
|
cyberpanel_rule.save()
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile("Added CyberPanel port 7080 to firewall database for UI visibility")
|
||||||
|
except Exception as e:
|
||||||
|
logging.CyberCPLogFileWriter.writeToFile(f"Failed to add CyberPanel port 7080 to database: {str(e)}")
|
||||||
|
|
||||||
|
# Refresh rules after potential creation
|
||||||
|
rules = FirewallRules.objects.all()
|
||||||
|
|
||||||
json_data = "["
|
json_data = "["
|
||||||
checker = 0
|
checker = 0
|
||||||
|
|
||||||
@@ -96,7 +120,6 @@ class FirewallManager:
|
|||||||
else:
|
else:
|
||||||
return ACLManager.loadErrorJson('add_status', 0)
|
return ACLManager.loadErrorJson('add_status', 0)
|
||||||
|
|
||||||
|
|
||||||
ruleName = data['ruleName']
|
ruleName = data['ruleName']
|
||||||
ruleProtocol = data['ruleProtocol']
|
ruleProtocol = data['ruleProtocol']
|
||||||
rulePort = data['rulePort']
|
rulePort = data['rulePort']
|
||||||
@@ -126,7 +149,6 @@ class FirewallManager:
|
|||||||
else:
|
else:
|
||||||
return ACLManager.loadErrorJson('delete_status', 0)
|
return ACLManager.loadErrorJson('delete_status', 0)
|
||||||
|
|
||||||
|
|
||||||
ruleID = data['id']
|
ruleID = data['id']
|
||||||
ruleProtocol = data['proto']
|
ruleProtocol = data['proto']
|
||||||
rulePort = data['port']
|
rulePort = data['port']
|
||||||
|
|||||||
829
guides/CUSTOM_CSS_GUIDE.md
Normal file
829
guides/CUSTOM_CSS_GUIDE.md
Normal file
@@ -0,0 +1,829 @@
|
|||||||
|
# CyberPanel 2.5.5-dev Custom CSS Guide
|
||||||
|
|
||||||
|
A comprehensive guide for creating custom CSS that fully works with the new design system of CyberPanel 2.5.5-dev.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Overview](#overview)
|
||||||
|
2. [Design System Architecture](#design-system-architecture)
|
||||||
|
3. [CSS Variables Reference](#css-variables-reference)
|
||||||
|
4. [Component Structure](#component-structure)
|
||||||
|
5. [Customization Examples](#customization-examples)
|
||||||
|
6. [Best Practices](#best-practices)
|
||||||
|
7. [Troubleshooting](#troubleshooting)
|
||||||
|
8. [Advanced Techniques](#advanced-techniques)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
CyberPanel 2.5.5-dev features a modern, CSS-variable-based design system that supports both light and dark themes. The system is built with:
|
||||||
|
|
||||||
|
- **CSS Custom Properties (Variables)** for consistent theming
|
||||||
|
- **Modern CSS Grid and Flexbox** layouts
|
||||||
|
- **Responsive design** principles
|
||||||
|
- **Dark mode support** with automatic theme switching
|
||||||
|
- **Component-based architecture** for easy customization
|
||||||
|
|
||||||
|
## Design System Architecture
|
||||||
|
|
||||||
|
### Core Structure
|
||||||
|
|
||||||
|
The design system is built around CSS custom properties defined in `:root` and `[data-theme="dark"]` selectors:
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
/* Light Theme Variables */
|
||||||
|
--bg-primary: #f0f0ff;
|
||||||
|
--bg-secondary: white;
|
||||||
|
--text-primary: #2f3640;
|
||||||
|
--accent-color: #5856d6;
|
||||||
|
/* ... more variables */
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] {
|
||||||
|
/* Dark Theme Variables */
|
||||||
|
--bg-primary: #0f0f23;
|
||||||
|
--bg-secondary: #1a1a3e;
|
||||||
|
--text-primary: #e4e4e7;
|
||||||
|
--accent-color: #7c7ff3;
|
||||||
|
/* ... more variables */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Components
|
||||||
|
|
||||||
|
1. **Header** (`#header`) - Top navigation bar
|
||||||
|
2. **Sidebar** (`#sidebar`) - Left navigation panel
|
||||||
|
3. **Main Content** (`#main-content`) - Page content area
|
||||||
|
4. **Cards** (`.content-card`) - Content containers
|
||||||
|
5. **Buttons** (`.btn`) - Interactive elements
|
||||||
|
6. **Forms** (`.form-*`) - Input elements
|
||||||
|
|
||||||
|
## CSS Variables Reference
|
||||||
|
|
||||||
|
### Color Variables
|
||||||
|
|
||||||
|
#### Background Colors
|
||||||
|
```css
|
||||||
|
--bg-primary /* Main background color */
|
||||||
|
--bg-secondary /* Card/container background */
|
||||||
|
--bg-sidebar /* Sidebar background */
|
||||||
|
--bg-sidebar-item /* Sidebar menu item background */
|
||||||
|
--bg-hover /* Hover state background */
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Text Colors
|
||||||
|
```css
|
||||||
|
--text-primary /* Main text color */
|
||||||
|
--text-secondary /* Secondary text color */
|
||||||
|
--text-heading /* Heading text color */
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Accent Colors
|
||||||
|
```css
|
||||||
|
--accent-color /* Primary accent color */
|
||||||
|
--accent-hover /* Accent hover state */
|
||||||
|
--danger-color /* Error/danger color */
|
||||||
|
--success-color /* Success color */
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Border & Shadow
|
||||||
|
```css
|
||||||
|
--border-color /* Default border color */
|
||||||
|
--shadow-color /* Default shadow color */
|
||||||
|
```
|
||||||
|
|
||||||
|
### Special Variables
|
||||||
|
|
||||||
|
#### Gradients
|
||||||
|
```css
|
||||||
|
--warning-bg /* Warning banner gradient */
|
||||||
|
--ai-banner-bg /* AI scanner banner gradient */
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Status Colors
|
||||||
|
```css
|
||||||
|
--success-bg /* Success background */
|
||||||
|
--success-border /* Success border */
|
||||||
|
--danger-bg /* Danger background */
|
||||||
|
--danger-border /* Danger border */
|
||||||
|
--warning-bg /* Warning background */
|
||||||
|
--info-bg /* Info background */
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Structure
|
||||||
|
|
||||||
|
### Header Component
|
||||||
|
|
||||||
|
```css
|
||||||
|
#header {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
height: 80px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 30px;
|
||||||
|
box-shadow: 0 2px 12px var(--shadow-color);
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 260px;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Customization Example:**
|
||||||
|
```css
|
||||||
|
/* Change header height and add custom styling */
|
||||||
|
#header {
|
||||||
|
height: 100px;
|
||||||
|
background: linear-gradient(135deg, var(--accent-color), var(--accent-hover));
|
||||||
|
border-bottom: 3px solid var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#header .logo-text .brand {
|
||||||
|
font-size: 32px;
|
||||||
|
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sidebar Component
|
||||||
|
|
||||||
|
```css
|
||||||
|
#sidebar {
|
||||||
|
width: 260px;
|
||||||
|
background: var(--bg-sidebar);
|
||||||
|
height: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 1001;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Customization Example:**
|
||||||
|
```css
|
||||||
|
/* Make sidebar wider with custom styling */
|
||||||
|
#sidebar {
|
||||||
|
width: 300px;
|
||||||
|
background: linear-gradient(180deg, var(--bg-sidebar), var(--bg-secondary));
|
||||||
|
border-right: 2px solid var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar .menu-item {
|
||||||
|
margin: 4px 20px;
|
||||||
|
border-radius: 12px;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar .menu-item:hover {
|
||||||
|
transform: translateX(5px);
|
||||||
|
box-shadow: 0 4px 12px var(--shadow-color);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Content Cards
|
||||||
|
|
||||||
|
```css
|
||||||
|
.content-card {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 30px;
|
||||||
|
box-shadow: 0 2px 8px var(--shadow-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Customization Example:**
|
||||||
|
```css
|
||||||
|
/* Add glassmorphism effect to cards */
|
||||||
|
.content-card {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .content-card {
|
||||||
|
background: rgba(26, 26, 62, 0.3);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Customization Examples
|
||||||
|
|
||||||
|
### 1. Complete Theme Override
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Custom Purple Theme */
|
||||||
|
:root {
|
||||||
|
--bg-primary: #f8f4ff;
|
||||||
|
--bg-secondary: #ffffff;
|
||||||
|
--bg-sidebar: #f3f0ff;
|
||||||
|
--bg-hover: #e8e0ff;
|
||||||
|
--text-primary: #2d1b69;
|
||||||
|
--text-secondary: #6b46c1;
|
||||||
|
--accent-color: #8b5cf6;
|
||||||
|
--accent-hover: #7c3aed;
|
||||||
|
--border-color: #e0d7ff;
|
||||||
|
--shadow-color: rgba(139, 92, 246, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--bg-primary: #1a0b2e;
|
||||||
|
--bg-secondary: #2d1b69;
|
||||||
|
--bg-sidebar: #1e0a3e;
|
||||||
|
--bg-hover: #3d2a7a;
|
||||||
|
--text-primary: #f3f0ff;
|
||||||
|
--text-secondary: #c4b5fd;
|
||||||
|
--accent-color: #a78bfa;
|
||||||
|
--accent-hover: #8b5cf6;
|
||||||
|
--border-color: #4c1d95;
|
||||||
|
--shadow-color: rgba(139, 92, 246, 0.3);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Custom Button Styles
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Custom button variants */
|
||||||
|
.btn-custom {
|
||||||
|
background: linear-gradient(135deg, var(--accent-color), var(--accent-hover));
|
||||||
|
border: none;
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-custom:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 20px rgba(139, 92, 246, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-custom:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Custom Sidebar Menu Items
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Animated sidebar menu items */
|
||||||
|
#sidebar .menu-item {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar .menu-item::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
|
||||||
|
transition: left 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar .menu-item:hover::before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar .menu-item .icon-wrapper {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Custom Form Styling
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Modern form inputs */
|
||||||
|
.form-control {
|
||||||
|
border: 2px solid var(--border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:focus {
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
box-shadow: 0 0 0 4px rgba(139, 92, 246, 0.1);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control::placeholder {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Custom Notifications
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Custom notification banners */
|
||||||
|
.notification-banner {
|
||||||
|
background: linear-gradient(135deg, var(--accent-color), var(--accent-hover));
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 20px;
|
||||||
|
margin: 20px;
|
||||||
|
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.3);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-banner::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 4px;
|
||||||
|
background: linear-gradient(90deg, #ff6b6b, #4ecdc4, #45b7d1, #96ceb4);
|
||||||
|
animation: rainbow 3s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rainbow {
|
||||||
|
0% { background-position: 0% 50%; }
|
||||||
|
50% { background-position: 100% 50%; }
|
||||||
|
100% { background-position: 0% 50%; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### 1. Use CSS Variables
|
||||||
|
|
||||||
|
Always use CSS variables instead of hardcoded values:
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* ✅ Good */
|
||||||
|
.custom-element {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ❌ Bad */
|
||||||
|
.custom-element {
|
||||||
|
background: white;
|
||||||
|
color: #2f3640;
|
||||||
|
border: 1px solid #e8e9ff;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Support Both Themes
|
||||||
|
|
||||||
|
Always provide both light and dark theme support:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.custom-element {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark theme specific adjustments */
|
||||||
|
[data-theme="dark"] .custom-element {
|
||||||
|
/* Additional dark theme styling if needed */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Use Semantic Class Names
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* ✅ Good */
|
||||||
|
.primary-button { }
|
||||||
|
.content-container { }
|
||||||
|
.navigation-item { }
|
||||||
|
|
||||||
|
/* ❌ Bad */
|
||||||
|
.red-button { }
|
||||||
|
.big-box { }
|
||||||
|
.item1 { }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Maintain Responsive Design
|
||||||
|
|
||||||
|
```css
|
||||||
|
.custom-element {
|
||||||
|
padding: 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.custom-element {
|
||||||
|
padding: 15px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Use Modern CSS Features
|
||||||
|
|
||||||
|
```css
|
||||||
|
.custom-element {
|
||||||
|
/* Use CSS Grid for layouts */
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
/* Use Flexbox for alignment */
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
/* Use CSS custom properties for calculations */
|
||||||
|
--element-height: 60px;
|
||||||
|
height: var(--element-height);
|
||||||
|
|
||||||
|
/* Use modern selectors */
|
||||||
|
&:hover { }
|
||||||
|
&:focus-within { }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
#### 1. Custom CSS Not Applying
|
||||||
|
|
||||||
|
**Problem:** Custom CSS doesn't appear to be working.
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
- Check CSS specificity - use more specific selectors
|
||||||
|
- Ensure CSS is placed after the base styles
|
||||||
|
- Use `!important` sparingly and only when necessary
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Increase specificity */
|
||||||
|
#main-content .content-card .custom-element {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Dark Mode Not Working
|
||||||
|
|
||||||
|
**Problem:** Custom styles don't adapt to dark mode.
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
- Always use CSS variables
|
||||||
|
- Test both light and dark themes
|
||||||
|
- Provide dark mode specific overrides when needed
|
||||||
|
|
||||||
|
```css
|
||||||
|
.custom-element {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .custom-element {
|
||||||
|
/* Dark mode specific adjustments */
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Responsive Issues
|
||||||
|
|
||||||
|
**Problem:** Custom styles break on mobile devices.
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
- Use relative units (rem, em, %)
|
||||||
|
- Test on different screen sizes
|
||||||
|
- Use CSS Grid and Flexbox for responsive layouts
|
||||||
|
|
||||||
|
```css
|
||||||
|
.custom-element {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.custom-element {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Techniques
|
||||||
|
|
||||||
|
### 1. CSS Animations
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Smooth page transitions */
|
||||||
|
.page-transition {
|
||||||
|
animation: fadeIn 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; transform: translateY(20px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover animations */
|
||||||
|
.interactive-element {
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.interactive-element:hover {
|
||||||
|
transform: translateY(-2px) scale(1.02);
|
||||||
|
box-shadow: 0 8px 25px var(--shadow-color);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. CSS Grid Layouts
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Advanced grid layouts */
|
||||||
|
.dashboard-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-card {
|
||||||
|
grid-column: span 1;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 2px 8px var(--shadow-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-card.featured {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.dashboard-card.featured {
|
||||||
|
grid-column: span 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. CSS Custom Properties with JavaScript
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Dynamic theming with CSS variables */
|
||||||
|
:root {
|
||||||
|
--custom-accent: #5856d6;
|
||||||
|
--custom-accent-hover: #4644c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-theme {
|
||||||
|
--accent-color: var(--custom-accent);
|
||||||
|
--accent-hover: var(--custom-accent-hover);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Advanced Selectors
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Complex selectors for specific styling */
|
||||||
|
#sidebar .menu-item:not(.active):hover {
|
||||||
|
background: var(--bg-hover);
|
||||||
|
transform: translateX(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card > *:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card > *:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attribute selectors */
|
||||||
|
[data-status="success"] {
|
||||||
|
border-left: 4px solid var(--success-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-status="error"] {
|
||||||
|
border-left: 4px solid var(--danger-color);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. CSS Functions and Calculations
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Using CSS functions */
|
||||||
|
.responsive-text {
|
||||||
|
font-size: clamp(14px, 2.5vw, 18px);
|
||||||
|
line-height: calc(1.5em + 0.5vw);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dynamic-spacing {
|
||||||
|
padding: calc(1rem + 2vw);
|
||||||
|
margin: calc(0.5rem + 1vw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CSS custom properties with calculations */
|
||||||
|
:root {
|
||||||
|
--base-size: 16px;
|
||||||
|
--scale-factor: 1.2;
|
||||||
|
--large-size: calc(var(--base-size) * var(--scale-factor));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Guide
|
||||||
|
|
||||||
|
### Step 1: Access the Design Page
|
||||||
|
|
||||||
|
1. Log into CyberPanel
|
||||||
|
2. Navigate to **Design** in the sidebar
|
||||||
|
3. Scroll down to the **Custom CSS** section
|
||||||
|
|
||||||
|
### Step 2: Add Your Custom CSS
|
||||||
|
|
||||||
|
1. Paste your custom CSS into the textarea
|
||||||
|
2. Click **Save Changes**
|
||||||
|
3. Refresh the page to see your changes
|
||||||
|
|
||||||
|
### Step 3: Test Your Changes
|
||||||
|
|
||||||
|
1. Test in both light and dark modes
|
||||||
|
2. Test on different screen sizes
|
||||||
|
3. Verify all interactive elements work correctly
|
||||||
|
|
||||||
|
### Step 4: Iterate and Refine
|
||||||
|
|
||||||
|
1. Make adjustments as needed
|
||||||
|
2. Test thoroughly before finalizing
|
||||||
|
3. Document your customizations
|
||||||
|
|
||||||
|
## Example: Complete Custom Theme
|
||||||
|
|
||||||
|
Here's a complete example of a custom theme that you can use as a starting point:
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Custom CyberPanel Theme - Ocean Blue */
|
||||||
|
|
||||||
|
/* Light Theme */
|
||||||
|
:root {
|
||||||
|
--bg-primary: #f0f9ff;
|
||||||
|
--bg-secondary: #ffffff;
|
||||||
|
--bg-sidebar: #e0f2fe;
|
||||||
|
--bg-sidebar-item: #ffffff;
|
||||||
|
--bg-hover: #bae6fd;
|
||||||
|
--text-primary: #0c4a6e;
|
||||||
|
--text-secondary: #0369a1;
|
||||||
|
--text-heading: #0c4a6e;
|
||||||
|
--border-color: #bae6fd;
|
||||||
|
--shadow-color: rgba(6, 105, 161, 0.1);
|
||||||
|
--accent-color: #0284c7;
|
||||||
|
--accent-hover: #0369a1;
|
||||||
|
--danger-color: #dc2626;
|
||||||
|
--success-color: #059669;
|
||||||
|
--warning-bg: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
|
||||||
|
--ai-banner-bg: linear-gradient(135deg, #0284c7 0%, #0369a1 50%, #0c4a6e 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark Theme */
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--bg-primary: #0c4a6e;
|
||||||
|
--bg-secondary: #075985;
|
||||||
|
--bg-sidebar: #0c4a6e;
|
||||||
|
--bg-sidebar-item: #075985;
|
||||||
|
--bg-hover: #0369a1;
|
||||||
|
--text-primary: #e0f2fe;
|
||||||
|
--text-secondary: #bae6fd;
|
||||||
|
--text-heading: #f0f9ff;
|
||||||
|
--border-color: #0369a1;
|
||||||
|
--shadow-color: rgba(0, 0, 0, 0.3);
|
||||||
|
--accent-color: #38bdf8;
|
||||||
|
--accent-hover: #0ea5e9;
|
||||||
|
--danger-color: #f87171;
|
||||||
|
--success-color: #34d399;
|
||||||
|
--warning-bg: linear-gradient(135deg, #78350f 0%, #92400e 100%);
|
||||||
|
--ai-banner-bg: linear-gradient(135deg, #0c4a6e 0%, #075985 50%, #0369a1 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Header Styling */
|
||||||
|
#header {
|
||||||
|
background: linear-gradient(135deg, var(--accent-color), var(--accent-hover));
|
||||||
|
box-shadow: 0 4px 20px var(--shadow-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#header .logo-text .brand {
|
||||||
|
color: white;
|
||||||
|
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Sidebar Styling */
|
||||||
|
#sidebar {
|
||||||
|
background: linear-gradient(180deg, var(--bg-sidebar), var(--bg-secondary));
|
||||||
|
border-right: 3px solid var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar .menu-item {
|
||||||
|
border-radius: 12px;
|
||||||
|
margin: 4px 20px;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar .menu-item:hover {
|
||||||
|
transform: translateX(8px);
|
||||||
|
box-shadow: 0 4px 15px var(--shadow-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar .menu-item.active {
|
||||||
|
background: linear-gradient(135deg, var(--accent-color), var(--accent-hover));
|
||||||
|
box-shadow: 0 4px 15px rgba(2, 132, 199, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Content Cards */
|
||||||
|
.content-card {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 4px 20px var(--shadow-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 4px;
|
||||||
|
background: linear-gradient(90deg, var(--accent-color), var(--accent-hover));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Buttons */
|
||||||
|
.btn {
|
||||||
|
border-radius: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: linear-gradient(135deg, var(--accent-color), var(--accent-hover));
|
||||||
|
box-shadow: 0 4px 15px rgba(2, 132, 199, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 20px rgba(2, 132, 199, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Form Elements */
|
||||||
|
.form-control {
|
||||||
|
border: 2px solid var(--border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:focus {
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
box-shadow: 0 0 0 4px rgba(2, 132, 199, 0.1);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Animations */
|
||||||
|
@keyframes slideIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card {
|
||||||
|
animation: slideIn 0.5s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
#sidebar {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card {
|
||||||
|
margin: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This guide provides everything you need to create beautiful, functional custom CSS for CyberPanel 2.5.5+. Remember to always test your changes thoroughly and use the CSS variables for consistency across themes.
|
||||||
327
guides/DEBIAN_13_INSTALLATION_GUIDE.md
Normal file
327
guides/DEBIAN_13_INSTALLATION_GUIDE.md
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
# Debian 13 Installation Guide for CyberPanel
|
||||||
|
|
||||||
|
## 🎯 Overview
|
||||||
|
|
||||||
|
This guide provides step-by-step instructions for installing CyberPanel on Debian 13 (Bookworm). Debian 13 support has been added to CyberPanel with full compatibility for package management, service configuration, and web server setup.
|
||||||
|
|
||||||
|
## 📋 Prerequisites
|
||||||
|
|
||||||
|
### System Requirements
|
||||||
|
- **OS**: Debian 13 (Bookworm) x86_64
|
||||||
|
- **RAM**: Minimum 1GB (2GB+ recommended)
|
||||||
|
- **Storage**: Minimum 10GB free space (20GB+ recommended)
|
||||||
|
- **CPU**: 2+ cores recommended
|
||||||
|
- **Network**: Internet connection required
|
||||||
|
|
||||||
|
### Supported Debian Versions
|
||||||
|
- ✅ **Debian 13** (Bookworm) - Full Support
|
||||||
|
- ✅ **Debian 12** (Bookworm) - Full Support
|
||||||
|
- ✅ **Debian 11** (Bullseye) - Full Support
|
||||||
|
|
||||||
|
## 🚀 Installation Steps
|
||||||
|
|
||||||
|
### Step 1: Update System
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update package lists
|
||||||
|
sudo apt update
|
||||||
|
|
||||||
|
# Upgrade system packages
|
||||||
|
sudo apt upgrade -y
|
||||||
|
|
||||||
|
# Install essential packages
|
||||||
|
sudo apt install -y curl wget git
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Download and Run CyberPanel Installer
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Download the latest CyberPanel installer
|
||||||
|
wget https://cyberpanel.sh/install.sh
|
||||||
|
|
||||||
|
# Make the installer executable
|
||||||
|
chmod +x install.sh
|
||||||
|
|
||||||
|
# Run the installer
|
||||||
|
sudo ./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Follow Installation Prompts
|
||||||
|
|
||||||
|
The installer will guide you through:
|
||||||
|
|
||||||
|
1. **License Agreement**: Accept the terms
|
||||||
|
2. **Installation Type**: Choose between:
|
||||||
|
- OpenLiteSpeed (Free)
|
||||||
|
- LiteSpeed Enterprise (Requires license)
|
||||||
|
3. **MySQL Configuration**:
|
||||||
|
- Single MySQL instance (recommended)
|
||||||
|
- Double MySQL instance (for high availability)
|
||||||
|
4. **Additional Services**:
|
||||||
|
- Postfix/Dovecot (Email server)
|
||||||
|
- PowerDNS (DNS server)
|
||||||
|
- PureFTPD (FTP server)
|
||||||
|
|
||||||
|
### Step 4: Verify Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check CyberPanel service status
|
||||||
|
sudo systemctl status lscpd
|
||||||
|
|
||||||
|
# Check web server status
|
||||||
|
sudo systemctl status apache2
|
||||||
|
|
||||||
|
# Check if CyberPanel is accessible
|
||||||
|
curl -I http://localhost:8090
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Post-Installation Configuration
|
||||||
|
|
||||||
|
### Access CyberPanel
|
||||||
|
|
||||||
|
1. Open your web browser
|
||||||
|
2. Navigate to: `http://your-server-ip:8090`
|
||||||
|
3. Default login credentials:
|
||||||
|
- **Username**: `admin`
|
||||||
|
- **Password**: `123456` (change immediately!)
|
||||||
|
|
||||||
|
### Change Default Password
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Login to CyberPanel CLI
|
||||||
|
sudo cyberpanel
|
||||||
|
|
||||||
|
# Change admin password
|
||||||
|
cyberpanel --change-password admin
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configure Firewall
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Allow CyberPanel ports
|
||||||
|
sudo ufw allow 8090/tcp
|
||||||
|
sudo ufw allow 80/tcp
|
||||||
|
sudo ufw allow 443/tcp
|
||||||
|
sudo ufw allow 21/tcp
|
||||||
|
sudo ufw allow 25/tcp
|
||||||
|
sudo ufw allow 53/tcp
|
||||||
|
sudo ufw allow 587/tcp
|
||||||
|
sudo ufw allow 993/tcp
|
||||||
|
sudo ufw allow 995/tcp
|
||||||
|
|
||||||
|
# Enable firewall
|
||||||
|
sudo ufw enable
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🐛 Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
#### 1. OS Detection Failed
|
||||||
|
**Problem**: Installer doesn't recognize Debian 13
|
||||||
|
**Solution**: Ensure you're running the latest installer version
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Download latest installer
|
||||||
|
wget https://cyberpanel.sh/install.sh
|
||||||
|
chmod +x install.sh
|
||||||
|
sudo ./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Package Installation Failed
|
||||||
|
**Problem**: apt-get errors during installation
|
||||||
|
**Solution**: Update repositories and retry
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update package lists
|
||||||
|
sudo apt update
|
||||||
|
|
||||||
|
# Fix broken packages
|
||||||
|
sudo apt --fix-broken install
|
||||||
|
|
||||||
|
# Retry installation
|
||||||
|
sudo ./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Service Won't Start
|
||||||
|
**Problem**: CyberPanel service fails to start
|
||||||
|
**Solution**: Check logs and restart services
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check service status
|
||||||
|
sudo systemctl status lscpd
|
||||||
|
|
||||||
|
# Check logs
|
||||||
|
sudo journalctl -u lscpd -f
|
||||||
|
|
||||||
|
# Restart service
|
||||||
|
sudo systemctl restart lscpd
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Web Server Issues
|
||||||
|
**Problem**: Apache2 configuration problems
|
||||||
|
**Solution**: Reconfigure web server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check Apache2 status
|
||||||
|
sudo systemctl status apache2
|
||||||
|
|
||||||
|
# Test configuration
|
||||||
|
sudo apache2ctl configtest
|
||||||
|
|
||||||
|
# Restart Apache2
|
||||||
|
sudo systemctl restart apache2
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log Files
|
||||||
|
|
||||||
|
Important log locations:
|
||||||
|
- **CyberPanel**: `/usr/local/CyberCP/logs/`
|
||||||
|
- **Apache2**: `/var/log/apache2/`
|
||||||
|
- **System**: `/var/log/syslog`
|
||||||
|
- **Installation**: `/root/cyberpanel-install.log`
|
||||||
|
|
||||||
|
## 🔒 Security Considerations
|
||||||
|
|
||||||
|
### Initial Security Setup
|
||||||
|
|
||||||
|
1. **Change Default Password**
|
||||||
|
```bash
|
||||||
|
sudo cyberpanel --change-password admin
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Update System**
|
||||||
|
```bash
|
||||||
|
sudo apt update && sudo apt upgrade -y
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Configure Firewall**
|
||||||
|
```bash
|
||||||
|
sudo ufw enable
|
||||||
|
sudo ufw default deny incoming
|
||||||
|
sudo ufw default allow outgoing
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Enable Fail2Ban**
|
||||||
|
```bash
|
||||||
|
sudo apt install fail2ban -y
|
||||||
|
sudo systemctl enable fail2ban
|
||||||
|
sudo systemctl start fail2ban
|
||||||
|
```
|
||||||
|
|
||||||
|
### SSL Certificate Setup
|
||||||
|
|
||||||
|
1. **Access CyberPanel Web Interface**
|
||||||
|
2. **Navigate to**: SSL → Let's Encrypt
|
||||||
|
3. **Enter your domain name**
|
||||||
|
4. **Click "Issue" to get free SSL certificate**
|
||||||
|
|
||||||
|
## 📊 Performance Optimization
|
||||||
|
|
||||||
|
### System Optimization
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Optimize Apache2 for Debian
|
||||||
|
sudo nano /etc/apache2/apache2.conf
|
||||||
|
|
||||||
|
# Add these lines:
|
||||||
|
ServerTokens Prod
|
||||||
|
ServerSignature Off
|
||||||
|
KeepAlive On
|
||||||
|
MaxKeepAliveRequests 100
|
||||||
|
KeepAliveTimeout 5
|
||||||
|
```
|
||||||
|
|
||||||
|
### PHP Optimization
|
||||||
|
|
||||||
|
1. **Access CyberPanel Web Interface**
|
||||||
|
2. **Navigate to**: PHP → PHP Settings
|
||||||
|
3. **Configure**:
|
||||||
|
- Memory limit: 256M
|
||||||
|
- Max execution time: 300
|
||||||
|
- Upload max filesize: 64M
|
||||||
|
|
||||||
|
## 🔄 Updates and Maintenance
|
||||||
|
|
||||||
|
### Update CyberPanel
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update to latest version
|
||||||
|
sudo cyberpanel --update
|
||||||
|
|
||||||
|
# Or use the upgrade script
|
||||||
|
sudo ./cyberpanel_upgrade.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### System Maintenance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update system packages
|
||||||
|
sudo apt update && sudo apt upgrade -y
|
||||||
|
|
||||||
|
# Clean package cache
|
||||||
|
sudo apt autoremove -y
|
||||||
|
sudo apt autoclean
|
||||||
|
|
||||||
|
# Check disk usage
|
||||||
|
df -h
|
||||||
|
|
||||||
|
# Check memory usage
|
||||||
|
free -h
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📚 Additional Resources
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- [CyberPanel Official Docs](https://cyberpanel.net/docs/)
|
||||||
|
- [Debian 13 Release Notes](https://www.debian.org/releases/bookworm/releasenotes)
|
||||||
|
- [Apache2 Configuration Guide](https://httpd.apache.org/docs/2.4/)
|
||||||
|
|
||||||
|
### Community Support
|
||||||
|
- [CyberPanel Community Forum](https://forums.cyberpanel.net/)
|
||||||
|
- [GitHub Issues](https://github.com/usmannasir/cyberpanel/issues)
|
||||||
|
- [Discord Server](https://discord.gg/cyberpanel)
|
||||||
|
|
||||||
|
### Testing Compatibility
|
||||||
|
|
||||||
|
Run the compatibility test script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Download test script
|
||||||
|
wget https://raw.githubusercontent.com/cyberpanel/cyberpanel/main/test_debian13_support.sh
|
||||||
|
|
||||||
|
# Make executable
|
||||||
|
chmod +x test_debian13_support.sh
|
||||||
|
|
||||||
|
# Run test
|
||||||
|
sudo ./test_debian13_support.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## ✅ Verification Checklist
|
||||||
|
|
||||||
|
After installation, verify these components:
|
||||||
|
|
||||||
|
- [ ] CyberPanel web interface accessible
|
||||||
|
- [ ] Admin password changed
|
||||||
|
- [ ] SSL certificate installed
|
||||||
|
- [ ] Firewall configured
|
||||||
|
- [ ] Email server working (if installed)
|
||||||
|
- [ ] DNS server working (if installed)
|
||||||
|
- [ ] FTP server working (if installed)
|
||||||
|
- [ ] System updates applied
|
||||||
|
- [ ] Logs are clean
|
||||||
|
- [ ] Services are running
|
||||||
|
|
||||||
|
## 🆘 Getting Help
|
||||||
|
|
||||||
|
If you encounter issues:
|
||||||
|
|
||||||
|
1. **Check the logs** (see Troubleshooting section)
|
||||||
|
2. **Run the compatibility test**
|
||||||
|
3. **Search the documentation**
|
||||||
|
4. **Ask in the community forum**
|
||||||
|
5. **Create a GitHub issue** with detailed information
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Note**: This guide is specifically for Debian 13. For other operating systems, refer to the main CyberPanel documentation.
|
||||||
@@ -14,6 +14,12 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu
|
|||||||
### 📧 Email & Marketing
|
### 📧 Email & Marketing
|
||||||
- **[Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md)** - Step-by-step guide for installing and configuring Mautic email marketing platform
|
- **[Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md)** - Step-by-step guide for installing and configuring Mautic email marketing platform
|
||||||
|
|
||||||
|
### 🐧 Operating System Support
|
||||||
|
- **[Debian 13 Installation Guide](DEBIAN_13_INSTALLATION_GUIDE.md)** - Complete installation and configuration guide for CyberPanel on Debian 13 (Bookworm)
|
||||||
|
|
||||||
|
### 🎨 Customization & Design
|
||||||
|
- **[Custom CSS Guide](CUSTOM_CSS_GUIDE.md)** - Complete guide for creating custom CSS that works with CyberPanel 2.5.5-dev design system
|
||||||
|
|
||||||
### 📖 General Documentation
|
### 📖 General Documentation
|
||||||
- **[README](../README.md)** - Main CyberPanel documentation with installation instructions and feature overview
|
- **[README](../README.md)** - Main CyberPanel documentation with installation instructions and feature overview
|
||||||
- **[Contributing Guide](CONTRIBUTING.md)** - Guidelines for contributing to the CyberPanel project
|
- **[Contributing Guide](CONTRIBUTING.md)** - Guidelines for contributing to the CyberPanel project
|
||||||
@@ -21,16 +27,20 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu
|
|||||||
## 🚀 Quick Start
|
## 🚀 Quick Start
|
||||||
|
|
||||||
1. **New to CyberPanel?** Start with the [README](../README.md) for installation and basic setup
|
1. **New to CyberPanel?** Start with the [README](../README.md) for installation and basic setup
|
||||||
2. **Need Docker help?** Check the [Docker Command Execution Guide](Docker_Command_Execution_Guide.md)
|
2. **Installing on Debian 13?** Follow the [Debian 13 Installation Guide](DEBIAN_13_INSTALLATION_GUIDE.md)
|
||||||
3. **Setting up email marketing?** Follow the [Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md)
|
3. **Need Docker help?** Check the [Docker Command Execution Guide](Docker_Command_Execution_Guide.md)
|
||||||
4. **Want to contribute?** Read the [Contributing Guide](CONTRIBUTING.md)
|
4. **Setting up email marketing?** Follow the [Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md)
|
||||||
|
5. **Want to customize the interface?** Check the [Custom CSS Guide](CUSTOM_CSS_GUIDE.md)
|
||||||
|
6. **Want to contribute?** Read the [Contributing Guide](CONTRIBUTING.md)
|
||||||
|
|
||||||
## 🔍 Finding What You Need
|
## 🔍 Finding What You Need
|
||||||
|
|
||||||
- **Installation & Setup**: [README](../README.md)
|
- **Installation & Setup**: [README](../README.md)
|
||||||
|
- **Debian 13 Installation**: [Debian 13 Installation Guide](DEBIAN_13_INSTALLATION_GUIDE.md)
|
||||||
- **Docker Features**: [Docker Command Execution Guide](Docker_Command_Execution_Guide.md)
|
- **Docker Features**: [Docker Command Execution Guide](Docker_Command_Execution_Guide.md)
|
||||||
- **Security Features**: [AI Scanner Documentation](AIScannerDocs.md)
|
- **Security Features**: [AI Scanner Documentation](AIScannerDocs.md)
|
||||||
- **Email Marketing**: [Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md)
|
- **Email Marketing**: [Mautic Installation Guide](MAUTIC_INSTALLATION_GUIDE.md)
|
||||||
|
- **Customization & Design**: [Custom CSS Guide](CUSTOM_CSS_GUIDE.md)
|
||||||
- **Development**: [Contributing Guide](CONTRIBUTING.md)
|
- **Development**: [Contributing Guide](CONTRIBUTING.md)
|
||||||
|
|
||||||
## 📝 Guide Categories
|
## 📝 Guide Categories
|
||||||
@@ -45,6 +55,11 @@ Welcome to the CyberPanel documentation hub! This folder contains all guides, tu
|
|||||||
- Third-party applications
|
- Third-party applications
|
||||||
- Custom configurations
|
- Custom configurations
|
||||||
|
|
||||||
|
### 🎨 **Customization**
|
||||||
|
- Custom CSS theming
|
||||||
|
- Design system integration
|
||||||
|
- Interface personalization
|
||||||
|
|
||||||
### 📖 **Documentation**
|
### 📖 **Documentation**
|
||||||
- Installation guides
|
- Installation guides
|
||||||
- Configuration tutorials
|
- Configuration tutorials
|
||||||
|
|||||||
@@ -1,190 +0,0 @@
|
|||||||
import os
|
|
||||||
import shutil
|
|
||||||
import pathlib
|
|
||||||
import stat
|
|
||||||
|
|
||||||
|
|
||||||
def mkdir_p(path, exist_ok=True):
|
|
||||||
"""
|
|
||||||
Creates the directory and paths leading up to it like unix mkdir -p .
|
|
||||||
Defaults to exist_ok so if it exists were not throwing fatal errors
|
|
||||||
https://docs.python.org/3.7/library/os.html#os.makedirs
|
|
||||||
"""
|
|
||||||
if not os.path.exists(path):
|
|
||||||
print('creating directory: ' + path)
|
|
||||||
os.makedirs(path, exist_ok)
|
|
||||||
|
|
||||||
|
|
||||||
def chmod_digit(file_path, perms):
|
|
||||||
"""
|
|
||||||
Helper function to chmod like you would in unix without having to preface 0o or converting to octal yourself.
|
|
||||||
Credits: https://stackoverflow.com/a/60052847/1621381
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
os.chmod(file_path, int(str(perms), base=8))
|
|
||||||
except:
|
|
||||||
print(f'Could not chmod : {file_path} to {perms}')
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def touch(filepath: str, exist_ok=True):
|
|
||||||
"""
|
|
||||||
Touches a file like unix `touch somefile` would.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
pathlib.Path(filepath).touch(exist_ok)
|
|
||||||
except FileExistsError:
|
|
||||||
print('Could touch : ' + filepath)
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def symlink(src, dst):
|
|
||||||
"""
|
|
||||||
Symlink a path to another if the src exists.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
if os.access(src, os.R_OK):
|
|
||||||
os.symlink(src, dst)
|
|
||||||
except:
|
|
||||||
print(f'Could not symlink Source: {src} > Destination: {dst}')
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def chown(path, user, group=-1):
|
|
||||||
"""
|
|
||||||
Chown file/path to user/group provided. Passing -1 to user or group will leave it unchanged.
|
|
||||||
Useful if just changing user or group vs both.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
shutil.chown(path, user, group)
|
|
||||||
except PermissionError:
|
|
||||||
print(f'Could not change permissions for: {path} to {user}:{group}')
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def recursive_chown(path, owner, group=-1):
|
|
||||||
"""
|
|
||||||
Recursively chown a path and contents to owner.
|
|
||||||
https://docs.python.org/3/library/shutil.html
|
|
||||||
"""
|
|
||||||
for dirpath, dirnames, filenames in os.walk(path):
|
|
||||||
try:
|
|
||||||
shutil.chown(dirpath, owner, group)
|
|
||||||
except PermissionError:
|
|
||||||
print('Could not change permissions for: ' + dirpath + ' to: ' + owner)
|
|
||||||
pass
|
|
||||||
for filename in filenames:
|
|
||||||
try:
|
|
||||||
shutil.chown(os.path.join(dirpath, filename), owner, group)
|
|
||||||
except PermissionError:
|
|
||||||
print('Could not change permissions for: ' + os.path.join(dirpath, filename) + ' to: ' + owner)
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def recursive_permissions(path, dir_mode=755, file_mode=644, topdir=True):
|
|
||||||
"""
|
|
||||||
Recursively chmod a path and contents to mode.
|
|
||||||
Defaults to chmod top level directory but can be optionally
|
|
||||||
toggled off when you want to chmod only contents of like a user's homedir vs homedir itself
|
|
||||||
https://docs.python.org/3.6/library/os.html#os.walk
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Here we are converting the integers to string and then to octal.
|
|
||||||
# so this function doesn't need to be called with 0o prefixed for the file and dir mode
|
|
||||||
dir_mode = int(str(dir_mode), base=8)
|
|
||||||
file_mode = int(str(file_mode), base=8)
|
|
||||||
|
|
||||||
if topdir:
|
|
||||||
# Set chmod on top level path
|
|
||||||
try:
|
|
||||||
os.chmod(path, dir_mode)
|
|
||||||
except:
|
|
||||||
print('Could not chmod :' + path + ' to ' + str(dir_mode))
|
|
||||||
for root, dirs, files in os.walk(path):
|
|
||||||
for d in dirs:
|
|
||||||
try:
|
|
||||||
os.chmod(os.path.join(root, d), dir_mode)
|
|
||||||
except:
|
|
||||||
print('Could not chmod :' + os.path.join(root, d) + ' to ' + str(dir_mode))
|
|
||||||
pass
|
|
||||||
for f in files:
|
|
||||||
try:
|
|
||||||
os.chmod(os.path.join(root, f), file_mode)
|
|
||||||
except:
|
|
||||||
print('Could not chmod :' + path + ' to ' + str(file_mode))
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
# Left intentionally here for reference.
|
|
||||||
# Set recursive chown for a path
|
|
||||||
# recursive_chown(my_path, 'root', 'root')
|
|
||||||
# for changing group recursively without affecting user
|
|
||||||
# recursive_chown('/usr/local/lscp/cyberpanel/rainloop/data', -1, 'lscpd')
|
|
||||||
|
|
||||||
# explicitly set permissions for directories/folders to 0755 and files to 0644
|
|
||||||
# recursive_permissions(my_path, 755, 644)
|
|
||||||
|
|
||||||
# Fix permissions and use default values
|
|
||||||
# recursive_permissions(my_path)
|
|
||||||
# =========================================================
|
|
||||||
# Below is a helper class for getting and working with permissions
|
|
||||||
# Original credits to : https://github.com/keysemble/perfm
|
|
||||||
|
|
||||||
def perm_octal_digit(rwx):
|
|
||||||
digit = 0
|
|
||||||
if rwx[0] == 'r':
|
|
||||||
digit += 4
|
|
||||||
if rwx[1] == 'w':
|
|
||||||
digit += 2
|
|
||||||
if rwx[2] == 'x':
|
|
||||||
digit += 1
|
|
||||||
return digit
|
|
||||||
|
|
||||||
|
|
||||||
class FilePerm:
|
|
||||||
def __init__(self, filepath):
|
|
||||||
filemode = stat.filemode(os.stat(filepath).st_mode)
|
|
||||||
permissions = [filemode[-9:][i:i + 3] for i in range(0, len(filemode[-9:]), 3)]
|
|
||||||
self.filepath = filepath
|
|
||||||
self.access_dict = dict(zip(['user', 'group', 'other'], [list(perm) for perm in permissions]))
|
|
||||||
|
|
||||||
def mode(self):
|
|
||||||
mode = 0
|
|
||||||
for shift, digit in enumerate(self.octal()[::-1]):
|
|
||||||
mode += digit << (shift * 3)
|
|
||||||
return mode
|
|
||||||
|
|
||||||
def digits(self):
|
|
||||||
"""Get the octal chmod equivalent value 755 in single string"""
|
|
||||||
return "".join(map(str, self.octal()))
|
|
||||||
|
|
||||||
def octal(self):
|
|
||||||
"""Get the octal value in a list [7, 5, 5]"""
|
|
||||||
return [perm_octal_digit(p) for p in self.access_dict.values()]
|
|
||||||
|
|
||||||
def access_bits(self, access):
|
|
||||||
if access in self.access_dict.keys():
|
|
||||||
r, w, x = self.access_dict[access]
|
|
||||||
return [r == 'r', w == 'w', x == 'x']
|
|
||||||
|
|
||||||
def update_bitwise(self, settings):
|
|
||||||
def perm_list(read=False, write=False, execute=False):
|
|
||||||
pl = ['-', '-', '-']
|
|
||||||
if read:
|
|
||||||
pl[0] = 'r'
|
|
||||||
if write:
|
|
||||||
pl[1] = 'w'
|
|
||||||
if execute:
|
|
||||||
pl[2] = 'x'
|
|
||||||
return pl
|
|
||||||
|
|
||||||
self.access_dict = dict(
|
|
||||||
[(access, perm_list(read=r, write=w, execute=x)) for access, [r, w, x] in settings.items()])
|
|
||||||
os.chmod(self.filepath, self.mode())
|
|
||||||
|
|
||||||
# project_directory = os.path.abspath(os.path.dirname(sys.argv[0]))
|
|
||||||
# home_directory = os.path.expanduser('~')
|
|
||||||
# print(f'Path: {home_directory} Mode: {FilePerm(home_directory).mode()} Octal: {FilePerm(home_directory).octal()} '
|
|
||||||
# f'Digits: {FilePerm(home_directory).digits()}')
|
|
||||||
# Example: Output
|
|
||||||
# Path: /home/cooluser Mode: 493 Octal: [7, 5, 5] Digits: 755
|
|
||||||
@@ -2969,7 +2969,7 @@ echo $oConfig->Save() ? 'Done' : 'Error';
|
|||||||
writeToFile.write(content)
|
writeToFile.write(content)
|
||||||
writeToFile.close()
|
writeToFile.close()
|
||||||
|
|
||||||
command = '/usr/local/lsws/lsphp72/bin/php /usr/local/CyberCP/public/snappymail.php'
|
command = '/usr/local/lsws/lsphp83/bin/php /usr/local/CyberCP/public/snappymail.php'
|
||||||
subprocess.call(shlex.split(command))
|
subprocess.call(shlex.split(command))
|
||||||
|
|
||||||
command = "chown -R lscpd:lscpd /usr/local/lscp/cyberpanel/snappymail/data"
|
command = "chown -R lscpd:lscpd /usr/local/lscp/cyberpanel/snappymail/data"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -44,6 +44,62 @@ FetchCloudLinuxAlmaVersionVersion = install_utils.FetchCloudLinuxAlmaVersionVers
|
|||||||
class InstallCyberPanel:
|
class InstallCyberPanel:
|
||||||
mysql_Root_password = ""
|
mysql_Root_password = ""
|
||||||
mysqlPassword = ""
|
mysqlPassword = ""
|
||||||
|
|
||||||
|
def is_almalinux9(self):
|
||||||
|
"""Check if running on AlmaLinux 9"""
|
||||||
|
if os.path.exists('/etc/almalinux-release'):
|
||||||
|
try:
|
||||||
|
with open('/etc/almalinux-release', 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
return 'release 9' in content
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
|
def fix_almalinux9_mariadb(self):
|
||||||
|
"""Fix AlmaLinux 9 MariaDB installation issues"""
|
||||||
|
if not self.is_almalinux9():
|
||||||
|
return
|
||||||
|
|
||||||
|
self.stdOut("Applying AlmaLinux 9 MariaDB fixes...", 1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Disable problematic MariaDB MaxScale repository
|
||||||
|
self.stdOut("Disabling problematic MariaDB MaxScale repository...", 1)
|
||||||
|
command = "dnf config-manager --disable mariadb-maxscale 2>/dev/null || true"
|
||||||
|
install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
|
||||||
|
|
||||||
|
# Remove problematic repository files
|
||||||
|
self.stdOut("Removing problematic repository files...", 1)
|
||||||
|
problematic_repos = [
|
||||||
|
'/etc/yum.repos.d/mariadb-maxscale.repo',
|
||||||
|
'/etc/yum.repos.d/mariadb-maxscale.repo.rpmnew'
|
||||||
|
]
|
||||||
|
for repo_file in problematic_repos:
|
||||||
|
if os.path.exists(repo_file):
|
||||||
|
os.remove(repo_file)
|
||||||
|
self.stdOut(f"Removed {repo_file}", 1)
|
||||||
|
|
||||||
|
# Clean DNF cache
|
||||||
|
self.stdOut("Cleaning DNF cache...", 1)
|
||||||
|
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_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
|
||||||
|
|
||||||
|
# Install MariaDB packages
|
||||||
|
self.stdOut("Installing MariaDB packages...", 1)
|
||||||
|
mariadb_packages = "MariaDB-server MariaDB-client MariaDB-backup MariaDB-devel"
|
||||||
|
command = f"dnf install -y {mariadb_packages}"
|
||||||
|
install_utils.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
|
||||||
|
|
||||||
|
self.stdOut("AlmaLinux 9 MariaDB fixes completed", 1)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.stdOut(f"Error applying AlmaLinux 9 MariaDB fixes: {str(e)}", 0)
|
||||||
CloudLinux8 = 0
|
CloudLinux8 = 0
|
||||||
|
|
||||||
def install_package(self, package_name, options=""):
|
def install_package(self, package_name, options=""):
|
||||||
@@ -335,7 +391,7 @@ class InstallCyberPanel:
|
|||||||
return self.reStartLiteSpeed()
|
return self.reStartLiteSpeed()
|
||||||
|
|
||||||
def installAllPHPVersions(self):
|
def installAllPHPVersions(self):
|
||||||
php_versions = ['71', '72', '73', '74', '80', '81', '82', '83']
|
php_versions = ['71', '72', '73', '74', '80', '81', '82', '83', '84', '85']
|
||||||
|
|
||||||
if self.distro == ubuntu:
|
if self.distro == ubuntu:
|
||||||
# Install base PHP 7.x packages
|
# Install base PHP 7.x packages
|
||||||
@@ -563,6 +619,12 @@ gpgcheck=1
|
|||||||
|
|
||||||
if self.remotemysql == 'OFF':
|
if self.remotemysql == 'OFF':
|
||||||
############## Start mariadb ######################
|
############## Start mariadb ######################
|
||||||
|
|
||||||
|
# Check if AlmaLinux 9 and apply fixes
|
||||||
|
if self.is_almalinux9():
|
||||||
|
self.stdOut("AlmaLinux 9 detected - applying MariaDB fixes", 1)
|
||||||
|
self.fix_almalinux9_mariadb()
|
||||||
|
|
||||||
self.manage_service('mariadb', 'start')
|
self.manage_service('mariadb', 'start')
|
||||||
|
|
||||||
############## Enable mariadb at system startup ######################
|
############## Enable mariadb at system startup ######################
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,121 +0,0 @@
|
|||||||
{% extends "baseTemplate/index.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% block title %}{% trans "Mail Functions - CyberPanel" %}{% endblock %}
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
{% load static %}
|
|
||||||
{% get_current_language as LANGUAGE_CODE %}
|
|
||||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div id="page-title">
|
|
||||||
<h2>{% trans "Mail Functions" %}</h2>
|
|
||||||
<p>{% trans "Manage email accounts on this page." %}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel col-md-12">
|
|
||||||
<div class="panel-body">
|
|
||||||
<h3 class="content-box-header">
|
|
||||||
{% trans "Available Functions" %}
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div class="example-box-wrapper">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-3 btn-min-width">
|
|
||||||
<a href="{% url 'createEmailAccount' %}" title="{% trans 'Create Email' %}"
|
|
||||||
class="tile-box tile-box-shortcut btn-primary">
|
|
||||||
<div class="tile-header">
|
|
||||||
{% trans "Create Email" %}
|
|
||||||
</div>
|
|
||||||
<div class="tile-content-wrapper">
|
|
||||||
<i class="fa fa-plus-square"></i>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-3 btn-min-width">
|
|
||||||
<a href="{% url 'listEmails' %}" title="{% trans 'List Emails' %}"
|
|
||||||
class="tile-box tile-box-shortcut btn-primary">
|
|
||||||
<div class="tile-header">
|
|
||||||
{% trans "List Emails" %}
|
|
||||||
</div>
|
|
||||||
<div class="tile-content-wrapper">
|
|
||||||
<i class="fa fa-plus-square"></i>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="col-md-3 btn-min-width">
|
|
||||||
<a href="{% url 'deleteEmailAccount' %}" title="{% trans 'Delete Email' %}"
|
|
||||||
class="tile-box tile-box-shortcut btn-primary">
|
|
||||||
<div class="tile-header">
|
|
||||||
{% trans "Delete Email" %}
|
|
||||||
</div>
|
|
||||||
<div class="tile-content-wrapper">
|
|
||||||
<i class="fa fa-trash"></i>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-3 btn-min-width">
|
|
||||||
<a href="{% url 'emailForwarding' %}" title="{% trans 'Email Forwarding' %}"
|
|
||||||
class="tile-box tile-box-shortcut btn-primary">
|
|
||||||
<div class="tile-header">
|
|
||||||
{% trans "Email Forwarding" %}
|
|
||||||
</div>
|
|
||||||
<div class="tile-content-wrapper">
|
|
||||||
<i class="fa fa-plus-square"></i>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="col-md-3 btn-min-width">
|
|
||||||
<a href="{% url 'changeEmailAccountPassword' %}" title="{% trans 'Change Password' %}"
|
|
||||||
class="tile-box tile-box-shortcut btn-primary">
|
|
||||||
<div class="tile-header">
|
|
||||||
{% trans "Change Password" %}
|
|
||||||
</div>
|
|
||||||
<div class="tile-content-wrapper">
|
|
||||||
<i class="fa fa-key"></i>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-3 btn-min-width">
|
|
||||||
<a href="{% url 'dkimManager' %}" title="{% trans 'DKIM Manager' %}"
|
|
||||||
class="tile-box tile-box-shortcut btn-primary">
|
|
||||||
<div class="tile-header">
|
|
||||||
{% trans "DKIM Manager" %}
|
|
||||||
</div>
|
|
||||||
<div class="tile-content-wrapper">
|
|
||||||
<i class="fa fa-key"></i>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-3 btn-min-width">
|
|
||||||
<a href="/snappymail/index.php" title="{% trans 'Access Webmail' %}"
|
|
||||||
class="tile-box tile-box-shortcut btn-primary">
|
|
||||||
<div class="tile-header">
|
|
||||||
{% trans "Access Webmail" %}
|
|
||||||
</div>
|
|
||||||
<div class="tile-content-wrapper">
|
|
||||||
<i class="fa fa-key"></i>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
25
phpmyadmin_htaccess
Normal file
25
phpmyadmin_htaccess
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# CyberPanel phpMyAdmin Access Control
|
||||||
|
# Place this file as /usr/local/CyberCP/public/phpmyadmin/.htaccess
|
||||||
|
|
||||||
|
# Enable rewrite engine
|
||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
# Check if user is not authenticated and redirect to login
|
||||||
|
RewriteCond %{HTTP_COOKIE} !sessionid=
|
||||||
|
RewriteRule ^(.*)$ /base/ [R=302,L]
|
||||||
|
|
||||||
|
# Additional security headers
|
||||||
|
Header always set X-Frame-Options DENY
|
||||||
|
Header always set X-Content-Type-Options nosniff
|
||||||
|
Header always set X-XSS-Protection "1; mode=block"
|
||||||
|
|
||||||
|
# Prevent direct access to sensitive files
|
||||||
|
<Files "config.inc.php">
|
||||||
|
Order Allow,Deny
|
||||||
|
Deny from all
|
||||||
|
</Files>
|
||||||
|
|
||||||
|
<Files "*.log">
|
||||||
|
Order Allow,Deny
|
||||||
|
Deny from all
|
||||||
|
</Files>
|
||||||
22
phpmyadmin_index_redirect.php
Normal file
22
phpmyadmin_index_redirect.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpMyAdmin Access Control - Direct Access Redirect
|
||||||
|
*
|
||||||
|
* This file should be placed at /usr/local/CyberCP/public/phpmyadmin/index.php
|
||||||
|
* to replace the default phpMyAdmin index.php and redirect unauthenticated users
|
||||||
|
* to the CyberPanel login page.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Check if user is logged into CyberPanel
|
||||||
|
session_start();
|
||||||
|
if (!isset($_SESSION['userID'])) {
|
||||||
|
// Redirect to CyberPanel login page
|
||||||
|
header('Location: /base/');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If user is authenticated, redirect to the actual phpMyAdmin interface
|
||||||
|
// through the proper CyberPanel route
|
||||||
|
header('Location: /dataBases/phpMyAdmin');
|
||||||
|
exit();
|
||||||
|
?>
|
||||||
@@ -1372,7 +1372,7 @@ echo $oConfig->Save() ? 'Done' : 'Error';
|
|||||||
command = 'chmod 640 /usr/local/lscp/cyberpanel/logs/access.log'
|
command = 'chmod 640 /usr/local/lscp/cyberpanel/logs/access.log'
|
||||||
ProcessUtilities.executioner(command, 'root', True)
|
ProcessUtilities.executioner(command, 'root', True)
|
||||||
|
|
||||||
command = '/usr/local/lsws/lsphp72/bin/php /usr/local/CyberCP/public/snappymail.php'
|
command = '/usr/local/lsws/lsphp83/bin/php /usr/local/CyberCP/public/snappymail.php'
|
||||||
ProcessUtilities.executioner(command, 'root', True)
|
ProcessUtilities.executioner(command, 'root', True)
|
||||||
|
|
||||||
command = 'chmod 600 /usr/local/CyberCP/public/snappymail.php'
|
command = 'chmod 600 /usr/local/CyberCP/public/snappymail.php'
|
||||||
|
|||||||
@@ -1,190 +0,0 @@
|
|||||||
import os
|
|
||||||
import shutil
|
|
||||||
import pathlib
|
|
||||||
import stat
|
|
||||||
|
|
||||||
|
|
||||||
def mkdir_p(path, exist_ok=True):
|
|
||||||
"""
|
|
||||||
Creates the directory and paths leading up to it like unix mkdir -p .
|
|
||||||
Defaults to exist_ok so if it exists were not throwing fatal errors
|
|
||||||
https://docs.python.org/3.7/library/os.html#os.makedirs
|
|
||||||
"""
|
|
||||||
if not os.path.exists(path):
|
|
||||||
print('creating directory: ' + path)
|
|
||||||
os.makedirs(path, exist_ok)
|
|
||||||
|
|
||||||
|
|
||||||
def chmod_digit(file_path, perms):
|
|
||||||
"""
|
|
||||||
Helper function to chmod like you would in unix without having to preface 0o or converting to octal yourself.
|
|
||||||
Credits: https://stackoverflow.com/a/60052847/1621381
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
os.chmod(file_path, int(str(perms), base=8))
|
|
||||||
except:
|
|
||||||
print(f'Could not chmod : {file_path} to {perms}')
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def touch(filepath: str, exist_ok=True):
|
|
||||||
"""
|
|
||||||
Touches a file like unix `touch somefile` would.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
pathlib.Path(filepath).touch(exist_ok)
|
|
||||||
except FileExistsError:
|
|
||||||
print('Could touch : ' + filepath)
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def symlink(src, dst):
|
|
||||||
"""
|
|
||||||
Symlink a path to another if the src exists.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
if os.access(src, os.R_OK):
|
|
||||||
os.symlink(src, dst)
|
|
||||||
except:
|
|
||||||
print(f'Could not symlink Source: {src} > Destination: {dst}')
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def chown(path, user, group=-1):
|
|
||||||
"""
|
|
||||||
Chown file/path to user/group provided. Passing -1 to user or group will leave it unchanged.
|
|
||||||
Useful if just changing user or group vs both.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
shutil.chown(path, user, group)
|
|
||||||
except PermissionError:
|
|
||||||
print(f'Could not change permissions for: {path} to {user}:{group}')
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def recursive_chown(path, owner, group=-1):
|
|
||||||
"""
|
|
||||||
Recursively chown a path and contents to owner.
|
|
||||||
https://docs.python.org/3/library/shutil.html
|
|
||||||
"""
|
|
||||||
for dirpath, dirnames, filenames in os.walk(path):
|
|
||||||
try:
|
|
||||||
shutil.chown(dirpath, owner, group)
|
|
||||||
except PermissionError:
|
|
||||||
print('Could not change permissions for: ' + dirpath + ' to: ' + owner)
|
|
||||||
pass
|
|
||||||
for filename in filenames:
|
|
||||||
try:
|
|
||||||
shutil.chown(os.path.join(dirpath, filename), owner, group)
|
|
||||||
except PermissionError:
|
|
||||||
print('Could not change permissions for: ' + os.path.join(dirpath, filename) + ' to: ' + owner)
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def recursive_permissions(path, dir_mode=755, file_mode=644, topdir=True):
|
|
||||||
"""
|
|
||||||
Recursively chmod a path and contents to mode.
|
|
||||||
Defaults to chmod top level directory but can be optionally
|
|
||||||
toggled off when you want to chmod only contents of like a user's homedir vs homedir itself
|
|
||||||
https://docs.python.org/3.6/library/os.html#os.walk
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Here we are converting the integers to string and then to octal.
|
|
||||||
# so this function doesn't need to be called with 0o prefixed for the file and dir mode
|
|
||||||
dir_mode = int(str(dir_mode), base=8)
|
|
||||||
file_mode = int(str(file_mode), base=8)
|
|
||||||
|
|
||||||
if topdir:
|
|
||||||
# Set chmod on top level path
|
|
||||||
try:
|
|
||||||
os.chmod(path, dir_mode)
|
|
||||||
except:
|
|
||||||
print('Could not chmod :' + path + ' to ' + str(dir_mode))
|
|
||||||
for root, dirs, files in os.walk(path):
|
|
||||||
for d in dirs:
|
|
||||||
try:
|
|
||||||
os.chmod(os.path.join(root, d), dir_mode)
|
|
||||||
except:
|
|
||||||
print('Could not chmod :' + os.path.join(root, d) + ' to ' + str(dir_mode))
|
|
||||||
pass
|
|
||||||
for f in files:
|
|
||||||
try:
|
|
||||||
os.chmod(os.path.join(root, f), file_mode)
|
|
||||||
except:
|
|
||||||
print('Could not chmod :' + path + ' to ' + str(file_mode))
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
# Left intentionally here for reference.
|
|
||||||
# Set recursive chown for a path
|
|
||||||
# recursive_chown(my_path, 'root', 'root')
|
|
||||||
# for changing group recursively without affecting user
|
|
||||||
# recursive_chown('/usr/local/lscp/cyberpanel/rainloop/data', -1, 'lscpd')
|
|
||||||
|
|
||||||
# explicitly set permissions for directories/folders to 0755 and files to 0644
|
|
||||||
# recursive_permissions(my_path, 755, 644)
|
|
||||||
|
|
||||||
# Fix permissions and use default values
|
|
||||||
# recursive_permissions(my_path)
|
|
||||||
# =========================================================
|
|
||||||
# Below is a helper class for getting and working with permissions
|
|
||||||
# Original credits to : https://github.com/keysemble/perfm
|
|
||||||
|
|
||||||
def perm_octal_digit(rwx):
|
|
||||||
digit = 0
|
|
||||||
if rwx[0] == 'r':
|
|
||||||
digit += 4
|
|
||||||
if rwx[1] == 'w':
|
|
||||||
digit += 2
|
|
||||||
if rwx[2] == 'x':
|
|
||||||
digit += 1
|
|
||||||
return digit
|
|
||||||
|
|
||||||
|
|
||||||
class FilePerm:
|
|
||||||
def __init__(self, filepath):
|
|
||||||
filemode = stat.filemode(os.stat(filepath).st_mode)
|
|
||||||
permissions = [filemode[-9:][i:i + 3] for i in range(0, len(filemode[-9:]), 3)]
|
|
||||||
self.filepath = filepath
|
|
||||||
self.access_dict = dict(zip(['user', 'group', 'other'], [list(perm) for perm in permissions]))
|
|
||||||
|
|
||||||
def mode(self):
|
|
||||||
mode = 0
|
|
||||||
for shift, digit in enumerate(self.octal()[::-1]):
|
|
||||||
mode += digit << (shift * 3)
|
|
||||||
return mode
|
|
||||||
|
|
||||||
def digits(self):
|
|
||||||
"""Get the octal chmod equivalent value 755 in single string"""
|
|
||||||
return "".join(map(str, self.octal()))
|
|
||||||
|
|
||||||
def octal(self):
|
|
||||||
"""Get the octal value in a list [7, 5, 5]"""
|
|
||||||
return [perm_octal_digit(p) for p in self.access_dict.values()]
|
|
||||||
|
|
||||||
def access_bits(self, access):
|
|
||||||
if access in self.access_dict.keys():
|
|
||||||
r, w, x = self.access_dict[access]
|
|
||||||
return [r == 'r', w == 'w', x == 'x']
|
|
||||||
|
|
||||||
def update_bitwise(self, settings):
|
|
||||||
def perm_list(read=False, write=False, execute=False):
|
|
||||||
pl = ['-', '-', '-']
|
|
||||||
if read:
|
|
||||||
pl[0] = 'r'
|
|
||||||
if write:
|
|
||||||
pl[1] = 'w'
|
|
||||||
if execute:
|
|
||||||
pl[2] = 'x'
|
|
||||||
return pl
|
|
||||||
|
|
||||||
self.access_dict = dict(
|
|
||||||
[(access, perm_list(read=r, write=w, execute=x)) for access, [r, w, x] in settings.items()])
|
|
||||||
os.chmod(self.filepath, self.mode())
|
|
||||||
|
|
||||||
# project_directory = os.path.abspath(os.path.dirname(sys.argv[0]))
|
|
||||||
# home_directory = os.path.expanduser('~')
|
|
||||||
# print(f'Path: {home_directory} Mode: {FilePerm(home_directory).mode()} Octal: {FilePerm(home_directory).octal()} '
|
|
||||||
# f'Digits: {FilePerm(home_directory).digits()}')
|
|
||||||
# Example: Output
|
|
||||||
# Path: /home/cooluser Mode: 493 Octal: [7, 5, 5] Digits: 755
|
|
||||||
@@ -1,5 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
// Check if user is logged into CyberPanel
|
||||||
|
session_start();
|
||||||
|
if (!isset($_SESSION['userID'])) {
|
||||||
|
// Redirect to CyberPanel login page
|
||||||
|
header('Location: /base/');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
define("PMA_SIGNON_INDEX", 1);
|
define("PMA_SIGNON_INDEX", 1);
|
||||||
|
|
||||||
|
|||||||
@@ -861,7 +861,7 @@ $cfg['Servers'][$i]['LogoutURL'] = 'phpmyadminsignin.php?logout';
|
|||||||
command = f'wget -q -O /usr/local/CyberCP/snappymail_cyberpanel.php https://raw.githubusercontent.com/the-djmaze/snappymail/master/integrations/cyberpanel/install.php'
|
command = f'wget -q -O /usr/local/CyberCP/snappymail_cyberpanel.php https://raw.githubusercontent.com/the-djmaze/snappymail/master/integrations/cyberpanel/install.php'
|
||||||
Upgrade.executioner_silent(command, 'verify certificate', 0)
|
Upgrade.executioner_silent(command, 'verify certificate', 0)
|
||||||
|
|
||||||
command = f'/usr/local/lsws/lsphp80/bin/php /usr/local/CyberCP/snappymail_cyberpanel.php'
|
command = f'/usr/local/lsws/lsphp83/bin/php /usr/local/CyberCP/snappymail_cyberpanel.php'
|
||||||
Upgrade.executioner_silent(command, 'verify certificate', 0)
|
Upgrade.executioner_silent(command, 'verify certificate', 0)
|
||||||
|
|
||||||
# labsPath = '/usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/configs/application.ini'
|
# labsPath = '/usr/local/lscp/cyberpanel/rainloop/data/_data_/_default_/configs/application.ini'
|
||||||
@@ -3121,7 +3121,7 @@ echo $oConfig->Save() ? 'Done' : 'Error';
|
|||||||
command = 'chmod 640 /usr/local/lscp/cyberpanel/logs/access.log'
|
command = 'chmod 640 /usr/local/lscp/cyberpanel/logs/access.log'
|
||||||
Upgrade.executioner(command, 0)
|
Upgrade.executioner(command, 0)
|
||||||
|
|
||||||
command = '/usr/local/lsws/lsphp72/bin/php /usr/local/CyberCP/public/snappymail.php'
|
command = '/usr/local/lsws/lsphp83/bin/php /usr/local/CyberCP/public/snappymail.php'
|
||||||
Upgrade.executioner_silent(command, 'Configure SnappyMail')
|
Upgrade.executioner_silent(command, 'Configure SnappyMail')
|
||||||
|
|
||||||
command = 'chmod 600 /usr/local/CyberCP/public/snappymail.php'
|
command = 'chmod 600 /usr/local/CyberCP/public/snappymail.php'
|
||||||
@@ -3182,44 +3182,221 @@ echo $oConfig->Save() ? 'Done' : 'Error';
|
|||||||
command = '/root/.acme.sh/acme.sh --set-default-ca --server letsencrypt'
|
command = '/root/.acme.sh/acme.sh --set-default-ca --server letsencrypt'
|
||||||
Upgrade.executioner(command, command, 0)
|
Upgrade.executioner(command, command, 0)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_package_availability(package_name):
|
||||||
|
"""Check if a package is available in the repositories"""
|
||||||
|
try:
|
||||||
|
# Try to search for the package without installing
|
||||||
|
if os.path.exists('/etc/yum.repos.d/') or os.path.exists('/etc/dnf/dnf.conf'):
|
||||||
|
# RHEL-based systems
|
||||||
|
command = f"dnf search --quiet {package_name} 2>/dev/null | grep -q '^Last metadata expiration' || yum search --quiet {package_name} 2>/dev/null | head -1"
|
||||||
|
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||||
|
return result.returncode == 0
|
||||||
|
else:
|
||||||
|
# Ubuntu/Debian systems
|
||||||
|
command = f"apt-cache search {package_name} 2>/dev/null | head -1"
|
||||||
|
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||||
|
return result.returncode == 0 and result.stdout.strip() != ""
|
||||||
|
except Exception as e:
|
||||||
|
Upgrade.stdOut(f"Error checking package availability for {package_name}: {str(e)}", 0)
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_almalinux9():
|
||||||
|
"""Check if running on AlmaLinux 9"""
|
||||||
|
if os.path.exists('/etc/almalinux-release'):
|
||||||
|
try:
|
||||||
|
with open('/etc/almalinux-release', 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
return 'release 9' in content
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def fix_almalinux9_mariadb():
|
||||||
|
"""Fix AlmaLinux 9 MariaDB installation issues"""
|
||||||
|
if not Upgrade.is_almalinux9():
|
||||||
|
return
|
||||||
|
|
||||||
|
Upgrade.stdOut("Applying AlmaLinux 9 MariaDB fixes...", 1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Disable problematic MariaDB MaxScale repository
|
||||||
|
Upgrade.stdOut("Disabling problematic MariaDB MaxScale repository...", 1)
|
||||||
|
command = "dnf config-manager --disable mariadb-maxscale 2>/dev/null || true"
|
||||||
|
subprocess.run(command, shell=True, capture_output=True)
|
||||||
|
|
||||||
|
# Remove problematic repository files
|
||||||
|
Upgrade.stdOut("Removing problematic repository files...", 1)
|
||||||
|
problematic_repos = [
|
||||||
|
'/etc/yum.repos.d/mariadb-maxscale.repo',
|
||||||
|
'/etc/yum.repos.d/mariadb-maxscale.repo.rpmnew'
|
||||||
|
]
|
||||||
|
for repo_file in problematic_repos:
|
||||||
|
if os.path.exists(repo_file):
|
||||||
|
os.remove(repo_file)
|
||||||
|
Upgrade.stdOut(f"Removed {repo_file}", 1)
|
||||||
|
|
||||||
|
# Clean DNF cache
|
||||||
|
Upgrade.stdOut("Cleaning DNF cache...", 1)
|
||||||
|
command = "dnf clean all"
|
||||||
|
subprocess.run(command, shell=True, capture_output=True)
|
||||||
|
|
||||||
|
# Install MariaDB from official repository
|
||||||
|
Upgrade.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'"
|
||||||
|
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
Upgrade.stdOut(f"Warning: MariaDB repo setup failed: {result.stderr}", 0)
|
||||||
|
|
||||||
|
# Install MariaDB packages
|
||||||
|
Upgrade.stdOut("Installing MariaDB packages...", 1)
|
||||||
|
mariadb_packages = "MariaDB-server MariaDB-client MariaDB-backup MariaDB-devel"
|
||||||
|
command = f"dnf install -y {mariadb_packages}"
|
||||||
|
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
Upgrade.stdOut(f"Warning: MariaDB installation issues: {result.stderr}", 0)
|
||||||
|
|
||||||
|
# Start and enable MariaDB service
|
||||||
|
Upgrade.stdOut("Starting MariaDB service...", 1)
|
||||||
|
services = ['mariadb', 'mysql', 'mysqld']
|
||||||
|
for service in services:
|
||||||
|
try:
|
||||||
|
command = f"systemctl start {service}"
|
||||||
|
result = subprocess.run(command, shell=True, capture_output=True)
|
||||||
|
if result.returncode == 0:
|
||||||
|
command = f"systemctl enable {service}"
|
||||||
|
subprocess.run(command, shell=True, capture_output=True)
|
||||||
|
Upgrade.stdOut(f"MariaDB service started as {service}", 1)
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
Upgrade.stdOut("AlmaLinux 9 MariaDB fixes completed", 1)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
Upgrade.stdOut(f"Error applying AlmaLinux 9 MariaDB fixes: {str(e)}", 0)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_available_php_versions():
|
||||||
|
"""Get list of available PHP versions based on OS"""
|
||||||
|
# Check for AlmaLinux 9+ first
|
||||||
|
if os.path.exists('/etc/almalinux-release'):
|
||||||
|
try:
|
||||||
|
with open('/etc/almalinux-release', 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
if 'release 9' in content or 'release 10' in content:
|
||||||
|
Upgrade.stdOut("AlmaLinux 9+ detected - checking available PHP versions", 1)
|
||||||
|
# AlmaLinux 9+ doesn't have PHP 7.1, 7.2, 7.3
|
||||||
|
php_versions = ['74', '80', '81', '82', '83', '84', '85']
|
||||||
|
else:
|
||||||
|
php_versions = ['71', '72', '73', '74', '80', '81', '82', '83', '84', '85']
|
||||||
|
except:
|
||||||
|
php_versions = ['71', '72', '73', '74', '80', '81', '82', '83', '84', '85']
|
||||||
|
else:
|
||||||
|
# Check other OS versions
|
||||||
|
os_info = Upgrade.findOperatingSytem()
|
||||||
|
if os_info in [Ubuntu24, CENTOS8]:
|
||||||
|
php_versions = ['74', '80', '81', '82', '83', '84', '85']
|
||||||
|
else:
|
||||||
|
php_versions = ['71', '72', '73', '74', '80', '81', '82', '83', '84', '85']
|
||||||
|
|
||||||
|
# Check availability of each version
|
||||||
|
available_versions = []
|
||||||
|
for version in php_versions:
|
||||||
|
if Upgrade.check_package_availability(f'lsphp{version}'):
|
||||||
|
available_versions.append(version)
|
||||||
|
else:
|
||||||
|
Upgrade.stdOut(f"PHP {version} not available on this OS", 0)
|
||||||
|
|
||||||
|
return available_versions
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def fixLiteSpeedConfig():
|
||||||
|
"""Fix LiteSpeed configuration issues by creating missing files"""
|
||||||
|
try:
|
||||||
|
Upgrade.stdOut("Checking and fixing LiteSpeed configuration...", 1)
|
||||||
|
|
||||||
|
# Check if LiteSpeed is installed
|
||||||
|
if not os.path.exists('/usr/local/lsws'):
|
||||||
|
Upgrade.stdOut("LiteSpeed not found at /usr/local/lsws", 0)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create missing configuration files
|
||||||
|
config_files = [
|
||||||
|
"/usr/local/lsws/conf/httpd_config.xml",
|
||||||
|
"/usr/local/lsws/conf/httpd.conf",
|
||||||
|
"/usr/local/lsws/conf/modsec.conf"
|
||||||
|
]
|
||||||
|
|
||||||
|
for config_file in config_files:
|
||||||
|
if not os.path.exists(config_file):
|
||||||
|
Upgrade.stdOut(f"Missing LiteSpeed config: {config_file}", 0)
|
||||||
|
|
||||||
|
# Create directory if it doesn't exist
|
||||||
|
os.makedirs(os.path.dirname(config_file), exist_ok=True)
|
||||||
|
|
||||||
|
# Create minimal config file
|
||||||
|
if config_file.endswith('httpd_config.xml'):
|
||||||
|
with open(config_file, 'w') as f:
|
||||||
|
f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
|
||||||
|
f.write('<httpServerConfig>\n')
|
||||||
|
f.write(' <!-- Minimal LiteSpeed configuration -->\n')
|
||||||
|
f.write(' <listener>\n')
|
||||||
|
f.write(' <name>Default</name>\n')
|
||||||
|
f.write(' <address>*:8088</address>\n')
|
||||||
|
f.write(' </listener>\n')
|
||||||
|
f.write('</httpServerConfig>\n')
|
||||||
|
elif config_file.endswith('httpd.conf'):
|
||||||
|
with open(config_file, 'w') as f:
|
||||||
|
f.write('# Minimal LiteSpeed HTTP configuration\n')
|
||||||
|
f.write('# This file will be updated by CyberPanel\n')
|
||||||
|
elif config_file.endswith('modsec.conf'):
|
||||||
|
with open(config_file, 'w') as f:
|
||||||
|
f.write('# ModSecurity configuration\n')
|
||||||
|
f.write('# This file will be updated by CyberPanel\n')
|
||||||
|
|
||||||
|
Upgrade.stdOut(f"Created minimal config: {config_file}", 1)
|
||||||
|
else:
|
||||||
|
Upgrade.stdOut(f"LiteSpeed config exists: {config_file}", 1)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
Upgrade.stdOut(f"Error fixing LiteSpeed config: {str(e)}", 0)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def installPHP73():
|
def installPHP73():
|
||||||
try:
|
try:
|
||||||
if Upgrade.installedOutput.find('lsphp73') == -1:
|
Upgrade.stdOut("Installing PHP versions based on OS compatibility...", 1)
|
||||||
command = 'yum install -y lsphp73 lsphp73-json lsphp73-xmlrpc lsphp73-xml lsphp73-tidy lsphp73-soap lsphp73-snmp ' \
|
|
||||||
'lsphp73-recode lsphp73-pspell lsphp73-process lsphp73-pgsql lsphp73-pear lsphp73-pdo lsphp73-opcache ' \
|
|
||||||
'lsphp73-odbc lsphp73-mysqlnd lsphp73-mcrypt lsphp73-mbstring lsphp73-ldap lsphp73-intl lsphp73-imap ' \
|
|
||||||
'lsphp73-gmp lsphp73-gd lsphp73-enchant lsphp73-dba lsphp73-common lsphp73-bcmath'
|
|
||||||
Upgrade.executioner(command, 'Install PHP 73, 0')
|
|
||||||
|
|
||||||
if Upgrade.installedOutput.find('lsphp74') == -1:
|
# Get available PHP versions
|
||||||
command = 'yum install -y lsphp74 lsphp74-json lsphp74-xmlrpc lsphp74-xml lsphp74-tidy lsphp74-soap lsphp74-snmp ' \
|
available_versions = Upgrade.get_available_php_versions()
|
||||||
'lsphp74-recode lsphp74-pspell lsphp74-process lsphp74-pgsql lsphp74-pear lsphp74-pdo lsphp74-opcache ' \
|
|
||||||
'lsphp74-odbc lsphp74-mysqlnd lsphp74-mcrypt lsphp74-mbstring lsphp74-ldap lsphp74-intl lsphp74-imap ' \
|
|
||||||
'lsphp74-gmp lsphp74-gd lsphp74-enchant lsphp74-dba lsphp74-common lsphp74-bcmath'
|
|
||||||
|
|
||||||
Upgrade.executioner(command, 'Install PHP 74, 0')
|
if not available_versions:
|
||||||
|
Upgrade.stdOut("No PHP versions available for installation", 0)
|
||||||
|
return
|
||||||
|
|
||||||
if Upgrade.installedOutput.find('lsphp80') == -1:
|
Upgrade.stdOut(f"Installing available PHP versions: {', '.join(available_versions)}", 1)
|
||||||
command = 'yum install lsphp80* -y'
|
|
||||||
|
for version in available_versions:
|
||||||
|
try:
|
||||||
|
if version in ['71', '72', '73', '74']:
|
||||||
|
# PHP 7.x versions with specific extensions
|
||||||
|
if Upgrade.installedOutput.find(f'lsphp{version}') == -1:
|
||||||
|
extensions = ['json', 'xmlrpc', 'xml', 'tidy', 'soap', 'snmp', 'recode', 'pspell', 'process', 'pgsql', 'pear', 'pdo', 'opcache', 'odbc', 'mysqlnd', 'mcrypt', 'mbstring', 'ldap', 'intl', 'imap', 'gmp', 'gd', 'enchant', 'dba', 'common', 'bcmath']
|
||||||
|
package_list = f"lsphp{version} " + " ".join([f"lsphp{version}-{ext}" for ext in extensions])
|
||||||
|
command = f"yum install -y {package_list}"
|
||||||
|
Upgrade.executioner(command, f'Install PHP {version}', 0)
|
||||||
|
else:
|
||||||
|
# PHP 8.x versions
|
||||||
|
if Upgrade.installedOutput.find(f'lsphp{version}') == -1:
|
||||||
|
command = f"yum install lsphp{version}* -y"
|
||||||
subprocess.call(command, shell=True)
|
subprocess.call(command, shell=True)
|
||||||
|
Upgrade.stdOut(f"Installed PHP {version}", 1)
|
||||||
|
|
||||||
if Upgrade.installedOutput.find('lsphp81') == -1:
|
except Exception as e:
|
||||||
command = 'yum install lsphp81* -y'
|
Upgrade.stdOut(f"Error installing PHP {version}: {str(e)}", 0)
|
||||||
subprocess.call(command, shell=True)
|
continue
|
||||||
|
|
||||||
if Upgrade.installedOutput.find('lsphp82') == -1:
|
|
||||||
command = 'yum install lsphp82* -y'
|
|
||||||
subprocess.call(command, shell=True)
|
|
||||||
|
|
||||||
command = 'yum install lsphp83* -y'
|
|
||||||
subprocess.call(command, shell=True)
|
|
||||||
|
|
||||||
command = 'yum install lsphp84* -y'
|
|
||||||
subprocess.call(command, shell=True)
|
|
||||||
|
|
||||||
command = 'yum install lsphp85* -y'
|
|
||||||
subprocess.call(command, shell=True)
|
|
||||||
|
|
||||||
except:
|
except:
|
||||||
command = 'DEBIAN_FRONTEND=noninteractive apt-get -y install ' \
|
command = 'DEBIAN_FRONTEND=noninteractive apt-get -y install ' \
|
||||||
@@ -3997,9 +4174,20 @@ pm.max_spare_servers = 3
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def setupPHPSymlink():
|
def setupPHPSymlink():
|
||||||
try:
|
try:
|
||||||
# Check if PHP 8.3 exists
|
# Try to find available PHP version (prioritize modern stable versions)
|
||||||
if not os.path.exists('/usr/local/lsws/lsphp83/bin/php'):
|
# Priority: 8.3 (recommended), 8.2, 8.4, 8.5, 8.1, 8.0, then older versions
|
||||||
Upgrade.stdOut("PHP 8.3 not found, installing it first...")
|
php_versions = ['83', '82', '84', '85', '81', '80', '74', '73', '72', '71']
|
||||||
|
selected_php = None
|
||||||
|
|
||||||
|
for version in php_versions:
|
||||||
|
if os.path.exists(f'/usr/local/lsws/lsphp{version}/bin/php'):
|
||||||
|
selected_php = version
|
||||||
|
Upgrade.stdOut(f"Found PHP {version}, using as default", 1)
|
||||||
|
break
|
||||||
|
|
||||||
|
if not selected_php:
|
||||||
|
# Try to install PHP 8.3 as fallback (modern stable version)
|
||||||
|
Upgrade.stdOut("No PHP found, installing PHP 8.3 as fallback...")
|
||||||
|
|
||||||
# Install PHP 8.3 based on OS
|
# Install PHP 8.3 based on OS
|
||||||
if os.path.exists(Upgrade.CentOSPath) or os.path.exists(Upgrade.openEulerPath):
|
if os.path.exists(Upgrade.CentOSPath) or os.path.exists(Upgrade.openEulerPath):
|
||||||
@@ -4013,16 +4201,17 @@ pm.max_spare_servers = 3
|
|||||||
if not os.path.exists('/usr/local/lsws/lsphp83/bin/php'):
|
if not os.path.exists('/usr/local/lsws/lsphp83/bin/php'):
|
||||||
Upgrade.stdOut('[ERROR] Failed to install PHP 8.3')
|
Upgrade.stdOut('[ERROR] Failed to install PHP 8.3')
|
||||||
return 0
|
return 0
|
||||||
|
selected_php = '83'
|
||||||
|
|
||||||
# Remove existing PHP symlink if it exists
|
# Remove existing PHP symlink if it exists
|
||||||
if os.path.exists('/usr/bin/php'):
|
if os.path.exists('/usr/bin/php'):
|
||||||
os.remove('/usr/bin/php')
|
os.remove('/usr/bin/php')
|
||||||
|
|
||||||
# Create symlink to PHP 8.3
|
# Create symlink to selected PHP version
|
||||||
command = 'ln -s /usr/local/lsws/lsphp83/bin/php /usr/bin/php'
|
command = f'ln -s /usr/local/lsws/lsphp{selected_php}/bin/php /usr/bin/php'
|
||||||
Upgrade.executioner(command, 'Setup PHP Symlink to 8.3', 0)
|
Upgrade.executioner(command, f'Setup PHP Symlink to {selected_php}', 0)
|
||||||
|
|
||||||
Upgrade.stdOut("PHP symlink updated to PHP 8.3 successfully.")
|
Upgrade.stdOut(f"PHP symlink updated to PHP {selected_php} successfully.")
|
||||||
|
|
||||||
except BaseException as msg:
|
except BaseException as msg:
|
||||||
Upgrade.stdOut('[ERROR] ' + str(msg) + " [setupPHPSymlink]")
|
Upgrade.stdOut('[ERROR] ' + str(msg) + " [setupPHPSymlink]")
|
||||||
@@ -4150,6 +4339,9 @@ pm.max_spare_servers = 3
|
|||||||
Upgrade.manageServiceMigrations()
|
Upgrade.manageServiceMigrations()
|
||||||
Upgrade.enableServices()
|
Upgrade.enableServices()
|
||||||
|
|
||||||
|
# Apply AlmaLinux 9 fixes before other installations
|
||||||
|
Upgrade.fix_almalinux9_mariadb()
|
||||||
|
|
||||||
Upgrade.installPHP73()
|
Upgrade.installPHP73()
|
||||||
Upgrade.setupCLI()
|
Upgrade.setupCLI()
|
||||||
Upgrade.someDirectories()
|
Upgrade.someDirectories()
|
||||||
@@ -4159,6 +4351,9 @@ pm.max_spare_servers = 3
|
|||||||
## Fix Apache configuration issues after upgrade
|
## Fix Apache configuration issues after upgrade
|
||||||
Upgrade.fixApacheConfiguration()
|
Upgrade.fixApacheConfiguration()
|
||||||
|
|
||||||
|
# Fix LiteSpeed configuration files if missing
|
||||||
|
Upgrade.fixLiteSpeedConfig()
|
||||||
|
|
||||||
### General migrations are not needed any more
|
### General migrations are not needed any more
|
||||||
|
|
||||||
# Upgrade.GeneralMigrations()
|
# Upgrade.GeneralMigrations()
|
||||||
@@ -4191,8 +4386,32 @@ pm.max_spare_servers = 3
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
command = 'cp /usr/local/lsws/lsphp80/bin/lsphp %s' % (phpPath)
|
# Try to find available PHP binary in order of preference (modern stable first)
|
||||||
|
php_versions = ['83', '82', '84', '85', '81', '80', '74', '73', '72', '71']
|
||||||
|
php_binary_found = False
|
||||||
|
|
||||||
|
for version in php_versions:
|
||||||
|
php_binary = f'/usr/local/lsws/lsphp{version}/bin/lsphp'
|
||||||
|
if os.path.exists(php_binary):
|
||||||
|
command = f'cp {php_binary} {phpPath}'
|
||||||
Upgrade.executioner(command, 0)
|
Upgrade.executioner(command, 0)
|
||||||
|
Upgrade.stdOut(f"Using PHP {version} for LSCPD", 1)
|
||||||
|
php_binary_found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not php_binary_found:
|
||||||
|
Upgrade.stdOut("Warning: No PHP binary found for LSCPD", 0)
|
||||||
|
# Try to create a symlink to any available PHP
|
||||||
|
try:
|
||||||
|
command = 'find /usr/local/lsws -name "lsphp" -type f 2>/dev/null | head -1'
|
||||||
|
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||||
|
if result.stdout.strip():
|
||||||
|
php_binary = result.stdout.strip()
|
||||||
|
command = f'cp {php_binary} {phpPath}'
|
||||||
|
Upgrade.executioner(command, 0)
|
||||||
|
Upgrade.stdOut(f"Using found PHP binary: {php_binary}", 1)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
if Upgrade.SoftUpgrade == 0:
|
if Upgrade.SoftUpgrade == 0:
|
||||||
try:
|
try:
|
||||||
@@ -4201,6 +4420,42 @@ pm.max_spare_servers = 3
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Try to start other services if they exist
|
||||||
|
# Enhanced service startup with AlmaLinux 9 support
|
||||||
|
services_to_start = ['fastapi_ssh_server', 'cyberpanel']
|
||||||
|
|
||||||
|
# Special handling for AlmaLinux 9 MariaDB service
|
||||||
|
if Upgrade.is_almalinux9():
|
||||||
|
Upgrade.stdOut("AlmaLinux 9 detected - applying enhanced service management", 1)
|
||||||
|
mariadb_services = ['mariadb', 'mysql', 'mysqld']
|
||||||
|
for service in mariadb_services:
|
||||||
|
try:
|
||||||
|
check_command = f"systemctl list-unit-files | grep -q {service}"
|
||||||
|
result = subprocess.run(check_command, shell=True, capture_output=True)
|
||||||
|
if result.returncode == 0:
|
||||||
|
command = f"systemctl restart {service}"
|
||||||
|
Upgrade.executioner(command, f'Restart {service} for AlmaLinux 9', 0)
|
||||||
|
command = f"systemctl enable {service}"
|
||||||
|
Upgrade.executioner(command, f'Enable {service} for AlmaLinux 9', 0)
|
||||||
|
Upgrade.stdOut(f"MariaDB service managed as {service} on AlmaLinux 9", 1)
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
Upgrade.stdOut(f"Could not manage MariaDB service {service}: {str(e)}", 0)
|
||||||
|
continue
|
||||||
|
|
||||||
|
for service in services_to_start:
|
||||||
|
try:
|
||||||
|
# Check if service exists
|
||||||
|
check_command = f"systemctl list-unit-files | grep -q {service}"
|
||||||
|
result = subprocess.run(check_command, shell=True, capture_output=True)
|
||||||
|
if result.returncode == 0:
|
||||||
|
command = f"systemctl start {service}"
|
||||||
|
Upgrade.executioner(command, f'Start {service}', 0)
|
||||||
|
else:
|
||||||
|
Upgrade.stdOut(f"Service {service} not found, skipping", 0)
|
||||||
|
except Exception as e:
|
||||||
|
Upgrade.stdOut(f"Could not start {service}: {str(e)}", 0)
|
||||||
|
|
||||||
# Remove CSF if installed and restore firewalld (CSF is being discontinued on August 31, 2025)
|
# Remove CSF if installed and restore firewalld (CSF is being discontinued on August 31, 2025)
|
||||||
if os.path.exists('/etc/csf'):
|
if os.path.exists('/etc/csf'):
|
||||||
print("CSF detected - removing CSF and restoring firewalld...")
|
print("CSF detected - removing CSF and restoring firewalld...")
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
49
rollback_phpmyadmin_redirect.sh
Normal file
49
rollback_phpmyadmin_redirect.sh
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# CyberPanel phpMyAdmin Access Control Rollback Script
|
||||||
|
# This script reverts the phpMyAdmin access control changes
|
||||||
|
|
||||||
|
echo "=== CyberPanel phpMyAdmin Access Control Rollback ==="
|
||||||
|
|
||||||
|
# Check if running as root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
echo "Please run this script as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find the most recent backup
|
||||||
|
LATEST_BACKUP=$(ls -t /usr/local/CyberCP/public/phpmyadmin/index.php.backup.* 2>/dev/null | head -n1)
|
||||||
|
|
||||||
|
if [ -z "$LATEST_BACKUP" ]; then
|
||||||
|
echo "No backup found. Cannot rollback changes."
|
||||||
|
echo "You may need to reinstall phpMyAdmin or restore from your own backup."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Found backup: $LATEST_BACKUP"
|
||||||
|
echo "Restoring original phpMyAdmin index.php..."
|
||||||
|
|
||||||
|
# Restore the original index.php
|
||||||
|
cp "$LATEST_BACKUP" /usr/local/CyberCP/public/phpmyadmin/index.php
|
||||||
|
|
||||||
|
# Remove the .htaccess file if it exists
|
||||||
|
if [ -f "/usr/local/CyberCP/public/phpmyadmin/.htaccess" ]; then
|
||||||
|
echo "Removing .htaccess file..."
|
||||||
|
rm /usr/local/CyberCP/public/phpmyadmin/.htaccess
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set proper permissions
|
||||||
|
echo "Setting permissions..."
|
||||||
|
chown lscpd:lscpd /usr/local/CyberCP/public/phpmyadmin/index.php
|
||||||
|
chmod 644 /usr/local/CyberCP/public/phpmyadmin/index.php
|
||||||
|
|
||||||
|
# Restart LiteSpeed to ensure changes take effect
|
||||||
|
echo "Restarting LiteSpeed..."
|
||||||
|
systemctl restart lscpd
|
||||||
|
|
||||||
|
echo "=== Rollback Complete ==="
|
||||||
|
echo ""
|
||||||
|
echo "phpMyAdmin access control has been reverted!"
|
||||||
|
echo "phpMyAdmin should now work as it did before the changes."
|
||||||
|
echo ""
|
||||||
|
echo "Backup file used: $LATEST_BACKUP"
|
||||||
28
run_migration.py
Normal file
28
run_migration.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Migration script for UserNotificationPreferences model
|
||||||
|
Run this script to apply the database migration for notification preferences
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import django
|
||||||
|
|
||||||
|
# Add the project directory to Python path
|
||||||
|
sys.path.append('/usr/local/CyberCP')
|
||||||
|
|
||||||
|
# Set up Django environment
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'CyberCP.settings')
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print("Running migration for UserNotificationPreferences...")
|
||||||
|
try:
|
||||||
|
execute_from_command_line(['manage.py', 'migrate', 'baseTemplate'])
|
||||||
|
print("Migration completed successfully!")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Migration failed: {e}")
|
||||||
|
sys.exit(1)
|
||||||
@@ -10,7 +10,9 @@ The CyberPanel Test Plugin is designed to work seamlessly across all CyberPanel-
|
|||||||
|------------------|---------|----------------|----------------|-----------------|-----------------|
|
|------------------|---------|----------------|----------------|-----------------|-----------------|
|
||||||
| **Ubuntu** | 22.04 | ✅ Full Support | 3.10+ | apt-get | systemctl |
|
| **Ubuntu** | 22.04 | ✅ Full Support | 3.10+ | apt-get | systemctl |
|
||||||
| **Ubuntu** | 20.04 | ✅ Full Support | 3.8+ | apt-get | systemctl |
|
| **Ubuntu** | 20.04 | ✅ Full Support | 3.8+ | apt-get | systemctl |
|
||||||
| **Debian** | 11+ | ✅ Full Support | 3.9+ | apt-get | systemctl |
|
| **Debian** | 13 | ✅ Full Support | 3.11+ | apt-get | systemctl |
|
||||||
|
| **Debian** | 12 | ✅ Full Support | 3.10+ | apt-get | systemctl |
|
||||||
|
| **Debian** | 11 | ✅ Full Support | 3.9+ | apt-get | systemctl |
|
||||||
| **AlmaLinux** | 10 | ✅ Full Support | 3.11+ | dnf | systemctl |
|
| **AlmaLinux** | 10 | ✅ Full Support | 3.11+ | dnf | systemctl |
|
||||||
| **AlmaLinux** | 9 | ✅ Full Support | 3.9+ | dnf | systemctl |
|
| **AlmaLinux** | 9 | ✅ Full Support | 3.9+ | dnf | systemctl |
|
||||||
| **AlmaLinux** | 8 | ✅ Full Support | 3.6+ | dnf/yum | systemctl |
|
| **AlmaLinux** | 8 | ✅ Full Support | 3.6+ | dnf/yum | systemctl |
|
||||||
@@ -457,6 +459,6 @@ sudo rm -f /home/cyberpanel/plugins/testPlugin
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Last Updated**: December 2024
|
**Last Updated**: September 2025
|
||||||
**Compatibility Version**: 1.0.0
|
**Compatibility Version**: 1.0.0
|
||||||
**Next Review**: March 2025
|
**Next Review**: March 2026
|
||||||
|
|||||||
430
test_debian13_support.sh
Normal file
430
test_debian13_support.sh
Normal file
@@ -0,0 +1,430 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Debian 13 Support Test Script for CyberPanel
|
||||||
|
# This script tests the compatibility of CyberPanel with Debian 13
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Test results
|
||||||
|
TESTS_PASSED=0
|
||||||
|
TESTS_FAILED=0
|
||||||
|
TESTS_TOTAL=0
|
||||||
|
|
||||||
|
# Function to print colored output
|
||||||
|
print_status() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||||
|
((TESTS_PASSED++))
|
||||||
|
}
|
||||||
|
|
||||||
|
print_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
((TESTS_FAILED++))
|
||||||
|
}
|
||||||
|
|
||||||
|
print_test_header() {
|
||||||
|
echo -e "\n${BLUE}=== $1 ===${NC}"
|
||||||
|
((TESTS_TOTAL++))
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to run a test
|
||||||
|
run_test() {
|
||||||
|
local test_name="$1"
|
||||||
|
local test_command="$2"
|
||||||
|
local expected_result="$3"
|
||||||
|
|
||||||
|
print_test_header "$test_name"
|
||||||
|
|
||||||
|
if eval "$test_command" >/dev/null 2>&1; then
|
||||||
|
if [[ "$expected_result" == "success" ]]; then
|
||||||
|
print_success "$test_name passed"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
print_error "$test_name failed (unexpected success)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [[ "$expected_result" == "failure" ]]; then
|
||||||
|
print_success "$test_name passed (expected failure)"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
print_error "$test_name failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check OS detection
|
||||||
|
test_os_detection() {
|
||||||
|
print_test_header "OS Detection Test"
|
||||||
|
|
||||||
|
# Check if we're on Debian 13
|
||||||
|
if [[ -f /etc/os-release ]]; then
|
||||||
|
source /etc/os-release
|
||||||
|
if [[ "$ID" == "debian" && "$VERSION_ID" == "13" ]]; then
|
||||||
|
print_success "Debian 13 detected correctly"
|
||||||
|
else
|
||||||
|
print_warning "Not running on Debian 13 (Current: $ID $VERSION_ID)"
|
||||||
|
print_status "This test is designed for Debian 13, but will continue with current OS"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_error "Cannot detect OS - /etc/os-release not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to test CyberPanel OS detection logic
|
||||||
|
test_cyberpanel_os_detection() {
|
||||||
|
print_test_header "CyberPanel OS Detection Logic Test"
|
||||||
|
|
||||||
|
# Test the OS detection logic from cyberpanel.sh
|
||||||
|
if grep -q -E "Debian GNU/Linux 11|Debian GNU/Linux 12|Debian GNU/Linux 13" /etc/os-release; then
|
||||||
|
print_success "CyberPanel OS detection logic recognizes Debian 11/12/13"
|
||||||
|
else
|
||||||
|
print_error "CyberPanel OS detection logic does not recognize current Debian version"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to test package manager compatibility
|
||||||
|
test_package_manager() {
|
||||||
|
print_test_header "Package Manager Compatibility Test"
|
||||||
|
|
||||||
|
# Test apt-get availability
|
||||||
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
|
print_success "apt-get package manager is available"
|
||||||
|
else
|
||||||
|
print_error "apt-get package manager not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test apt-get update (dry run)
|
||||||
|
if apt-get update --dry-run >/dev/null 2>&1; then
|
||||||
|
print_success "apt-get update works correctly"
|
||||||
|
else
|
||||||
|
print_warning "apt-get update failed (may be network related)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to test systemd compatibility
|
||||||
|
test_systemd_compatibility() {
|
||||||
|
print_test_header "Systemd Compatibility Test"
|
||||||
|
|
||||||
|
# Test systemctl availability
|
||||||
|
if command -v systemctl >/dev/null 2>&1; then
|
||||||
|
print_success "systemctl is available"
|
||||||
|
else
|
||||||
|
print_error "systemctl not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test systemd status
|
||||||
|
if systemctl is-system-running >/dev/null 2>&1; then
|
||||||
|
print_success "systemd is running"
|
||||||
|
else
|
||||||
|
print_warning "systemd status unclear"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to test Python compatibility
|
||||||
|
test_python_compatibility() {
|
||||||
|
print_test_header "Python Compatibility Test"
|
||||||
|
|
||||||
|
# Test Python 3 availability
|
||||||
|
if command -v python3 >/dev/null 2>&1; then
|
||||||
|
local python_version=$(python3 --version 2>&1 | cut -d' ' -f2)
|
||||||
|
print_success "Python 3 is available: $python_version"
|
||||||
|
|
||||||
|
# Check if Python version is compatible (3.6+)
|
||||||
|
local major_version=$(echo "$python_version" | cut -d'.' -f1)
|
||||||
|
local minor_version=$(echo "$python_version" | cut -d'.' -f2)
|
||||||
|
|
||||||
|
if [[ $major_version -ge 3 && $minor_version -ge 6 ]]; then
|
||||||
|
print_success "Python version is compatible (3.6+)"
|
||||||
|
else
|
||||||
|
print_warning "Python version may not be fully compatible (requires 3.6+)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_error "Python 3 not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test pip3 availability
|
||||||
|
if command -v pip3 >/dev/null 2>&1; then
|
||||||
|
print_success "pip3 is available"
|
||||||
|
else
|
||||||
|
print_warning "pip3 not found (may need to be installed)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to test web server compatibility
|
||||||
|
test_web_server_compatibility() {
|
||||||
|
print_test_header "Web Server Compatibility Test"
|
||||||
|
|
||||||
|
# Test Apache2 availability
|
||||||
|
if command -v apache2 >/dev/null 2>&1; then
|
||||||
|
print_success "Apache2 is available"
|
||||||
|
else
|
||||||
|
print_warning "Apache2 not found (will be installed by CyberPanel)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test if Apache2 can be installed
|
||||||
|
if apt-cache show apache2 >/dev/null 2>&1; then
|
||||||
|
print_success "Apache2 package is available in repositories"
|
||||||
|
else
|
||||||
|
print_error "Apache2 package not found in repositories"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to test required packages availability
|
||||||
|
test_required_packages() {
|
||||||
|
print_test_header "Required Packages Availability Test"
|
||||||
|
|
||||||
|
local required_packages=(
|
||||||
|
"curl"
|
||||||
|
"wget"
|
||||||
|
"git"
|
||||||
|
"build-essential"
|
||||||
|
"python3-dev"
|
||||||
|
"python3-pip"
|
||||||
|
"python3-venv"
|
||||||
|
"software-properties-common"
|
||||||
|
"apt-transport-https"
|
||||||
|
"ca-certificates"
|
||||||
|
"gnupg"
|
||||||
|
)
|
||||||
|
|
||||||
|
local available_count=0
|
||||||
|
local total_count=${#required_packages[@]}
|
||||||
|
|
||||||
|
for package in "${required_packages[@]}"; do
|
||||||
|
if apt-cache show "$package" >/dev/null 2>&1; then
|
||||||
|
print_success "$package is available"
|
||||||
|
((available_count++))
|
||||||
|
else
|
||||||
|
print_warning "$package not found in repositories"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
print_status "Available packages: $available_count/$total_count"
|
||||||
|
|
||||||
|
if [[ $available_count -ge $((total_count * 8 / 10)) ]]; then
|
||||||
|
print_success "Most required packages are available"
|
||||||
|
else
|
||||||
|
print_warning "Many required packages are missing"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to test LiteSpeed repository compatibility
|
||||||
|
test_litespeed_repo_compatibility() {
|
||||||
|
print_test_header "LiteSpeed Repository Compatibility Test"
|
||||||
|
|
||||||
|
# Test if we can access LiteSpeed Debian repository
|
||||||
|
if curl -s --head "http://rpms.litespeedtech.com/debian/" | head -n 1 | grep -q "200 OK"; then
|
||||||
|
print_success "LiteSpeed Debian repository is accessible"
|
||||||
|
else
|
||||||
|
print_warning "LiteSpeed Debian repository may not be accessible"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test if we can download the repository setup script
|
||||||
|
if wget --spider "http://rpms.litespeedtech.com/debian/enable_lst_debian_repo.sh" 2>/dev/null; then
|
||||||
|
print_success "LiteSpeed repository setup script is available"
|
||||||
|
else
|
||||||
|
print_warning "LiteSpeed repository setup script may not be available"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to test MariaDB compatibility
|
||||||
|
test_mariadb_compatibility() {
|
||||||
|
print_test_header "MariaDB Compatibility Test"
|
||||||
|
|
||||||
|
# Test MariaDB repository accessibility
|
||||||
|
if curl -s --head "https://mariadb.org/mariadb_release_signing_key.pgp" | head -n 1 | grep -q "200 OK"; then
|
||||||
|
print_success "MariaDB signing key is accessible"
|
||||||
|
else
|
||||||
|
print_warning "MariaDB signing key may not be accessible"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test if MariaDB packages are available
|
||||||
|
if apt-cache show mariadb-server >/dev/null 2>&1; then
|
||||||
|
print_success "MariaDB packages are available in repositories"
|
||||||
|
else
|
||||||
|
print_warning "MariaDB packages not found in default repositories"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to test network connectivity
|
||||||
|
test_network_connectivity() {
|
||||||
|
print_test_header "Network Connectivity Test"
|
||||||
|
|
||||||
|
local test_urls=(
|
||||||
|
"https://github.com"
|
||||||
|
"https://pypi.org"
|
||||||
|
"http://rpms.litespeedtech.com"
|
||||||
|
"https://mariadb.org"
|
||||||
|
)
|
||||||
|
|
||||||
|
local accessible_count=0
|
||||||
|
local total_count=${#test_urls[@]}
|
||||||
|
|
||||||
|
for url in "${test_urls[@]}"; do
|
||||||
|
if curl -s --head "$url" >/dev/null 2>&1; then
|
||||||
|
print_success "$url is accessible"
|
||||||
|
((accessible_count++))
|
||||||
|
else
|
||||||
|
print_warning "$url is not accessible"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
print_status "Accessible URLs: $accessible_count/$total_count"
|
||||||
|
|
||||||
|
if [[ $accessible_count -ge $((total_count * 3 / 4)) ]]; then
|
||||||
|
print_success "Network connectivity is good"
|
||||||
|
else
|
||||||
|
print_warning "Network connectivity may be limited"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to test system resources
|
||||||
|
test_system_resources() {
|
||||||
|
print_test_header "System Resources Test"
|
||||||
|
|
||||||
|
# Test available memory
|
||||||
|
local total_memory=$(free -m | awk 'NR==2{print $2}')
|
||||||
|
if [[ $total_memory -ge 1024 ]]; then
|
||||||
|
print_success "Sufficient memory available: ${total_memory}MB"
|
||||||
|
else
|
||||||
|
print_warning "Low memory: ${total_memory}MB (recommended: 1GB+)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test available disk space
|
||||||
|
local available_space=$(df / | awk 'NR==2{print $4}')
|
||||||
|
local available_gb=$((available_space / 1024 / 1024))
|
||||||
|
if [[ $available_gb -ge 10 ]]; then
|
||||||
|
print_success "Sufficient disk space: ${available_gb}GB"
|
||||||
|
else
|
||||||
|
print_warning "Low disk space: ${available_gb}GB (recommended: 10GB+)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test CPU cores
|
||||||
|
local cpu_cores=$(nproc)
|
||||||
|
if [[ $cpu_cores -ge 2 ]]; then
|
||||||
|
print_success "Sufficient CPU cores: $cpu_cores"
|
||||||
|
else
|
||||||
|
print_warning "Limited CPU cores: $cpu_cores (recommended: 2+)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to run all tests
|
||||||
|
run_all_tests() {
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo -e "${BLUE} CyberPanel Debian 13 Compatibility Test${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}\n"
|
||||||
|
|
||||||
|
test_os_detection
|
||||||
|
test_cyberpanel_os_detection
|
||||||
|
test_package_manager
|
||||||
|
test_systemd_compatibility
|
||||||
|
test_python_compatibility
|
||||||
|
test_web_server_compatibility
|
||||||
|
test_required_packages
|
||||||
|
test_litespeed_repo_compatibility
|
||||||
|
test_mariadb_compatibility
|
||||||
|
test_network_connectivity
|
||||||
|
test_system_resources
|
||||||
|
|
||||||
|
echo -e "\n${BLUE}========================================${NC}"
|
||||||
|
echo -e "${BLUE} Test Results Summary${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo -e "Total Tests: $TESTS_TOTAL"
|
||||||
|
echo -e "Passed: ${GREEN}$TESTS_PASSED${NC}"
|
||||||
|
echo -e "Failed: ${RED}$TESTS_FAILED${NC}"
|
||||||
|
|
||||||
|
if [[ $TESTS_FAILED -eq 0 ]]; then
|
||||||
|
echo -e "\n${GREEN}✅ All tests passed! Debian 13 appears to be compatible with CyberPanel.${NC}"
|
||||||
|
return 0
|
||||||
|
elif [[ $TESTS_FAILED -le 2 ]]; then
|
||||||
|
echo -e "\n${YELLOW}⚠️ Most tests passed. Debian 13 should be compatible with minor issues.${NC}"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e "\n${RED}❌ Multiple tests failed. Debian 13 may have compatibility issues.${NC}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to show usage
|
||||||
|
show_usage() {
|
||||||
|
echo "Usage: $0 [OPTIONS]"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " -h, --help Show this help message"
|
||||||
|
echo " -v, --verbose Enable verbose output"
|
||||||
|
echo " --quick Run only essential tests"
|
||||||
|
echo ""
|
||||||
|
echo "This script tests CyberPanel compatibility with Debian 13."
|
||||||
|
echo "Run as root for best results."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main execution
|
||||||
|
main() {
|
||||||
|
local verbose=false
|
||||||
|
local quick=false
|
||||||
|
|
||||||
|
# Parse command line arguments
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-h|--help)
|
||||||
|
show_usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-v|--verbose)
|
||||||
|
verbose=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--quick)
|
||||||
|
quick=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option: $1"
|
||||||
|
show_usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check if running as root
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
print_warning "Not running as root. Some tests may fail."
|
||||||
|
print_status "Consider running: sudo $0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
if [[ "$quick" == "true" ]]; then
|
||||||
|
print_status "Running quick compatibility test..."
|
||||||
|
test_os_detection
|
||||||
|
test_cyberpanel_os_detection
|
||||||
|
test_package_manager
|
||||||
|
test_systemd_compatibility
|
||||||
|
else
|
||||||
|
run_all_tests
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run main function
|
||||||
|
main "$@"
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
# CyberPanel Secure Installation Guide
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This document describes the secure installation process for CyberPanel that eliminates hardcoded passwords and implements environment-based configuration.
|
|
||||||
|
|
||||||
## Security Improvements
|
|
||||||
|
|
||||||
### ✅ **Fixed Security Vulnerabilities**
|
|
||||||
|
|
||||||
1. **Hardcoded Database Passwords** - Now generated securely during installation
|
|
||||||
2. **Hardcoded Django Secret Key** - Now generated using cryptographically secure random generation
|
|
||||||
3. **Environment Variables** - All sensitive configuration moved to `.env` file
|
|
||||||
4. **File Permissions** - `.env` file set to 600 (owner read/write only)
|
|
||||||
|
|
||||||
### 🔐 **Security Features**
|
|
||||||
|
|
||||||
- **Cryptographically Secure Passwords**: Uses Python's `secrets` module for password generation
|
|
||||||
- **Environment-based Configuration**: Sensitive data stored in `.env` file, not in code
|
|
||||||
- **Secure File Permissions**: Environment files protected with 600 permissions
|
|
||||||
- **Credential Backup**: Automatic backup of credentials for recovery
|
|
||||||
- **Fallback Security**: Maintains backward compatibility with fallback method
|
|
||||||
|
|
||||||
## Installation Process
|
|
||||||
|
|
||||||
### 1. **Automatic Secure Installation**
|
|
||||||
|
|
||||||
The installation script now automatically:
|
|
||||||
|
|
||||||
1. Generates secure random passwords for:
|
|
||||||
- MySQL root user
|
|
||||||
- CyberPanel database user
|
|
||||||
- Django secret key
|
|
||||||
|
|
||||||
2. Creates `.env` file with secure configuration:
|
|
||||||
```bash
|
|
||||||
# Generated during installation
|
|
||||||
SECRET_KEY=your_64_character_secure_key
|
|
||||||
DB_PASSWORD=your_24_character_secure_password
|
|
||||||
ROOT_DB_PASSWORD=your_24_character_secure_password
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Creates `.env.backup` file for credential recovery
|
|
||||||
4. Sets secure file permissions (600) on all environment files
|
|
||||||
|
|
||||||
### 2. **Manual Installation** (if needed)
|
|
||||||
|
|
||||||
If you need to manually generate environment configuration:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /usr/local/CyberCP
|
|
||||||
python install/env_generator.py /usr/local/CyberCP
|
|
||||||
```
|
|
||||||
|
|
||||||
## File Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
/usr/local/CyberCP/
|
|
||||||
├── .env # Main environment configuration (600 permissions)
|
|
||||||
├── .env.backup # Credential backup (600 permissions)
|
|
||||||
├── .env.template # Template for manual configuration
|
|
||||||
├── .gitignore # Prevents .env files from being committed
|
|
||||||
└── CyberCP/
|
|
||||||
└── settings.py # Updated to use environment variables
|
|
||||||
```
|
|
||||||
|
|
||||||
## Security Best Practices
|
|
||||||
|
|
||||||
### ✅ **Do's**
|
|
||||||
|
|
||||||
- Keep `.env` and `.env.backup` files secure
|
|
||||||
- Record credentials from `.env.backup` and delete the file after installation
|
|
||||||
- Use strong, unique passwords for production deployments
|
|
||||||
- Regularly rotate database passwords
|
|
||||||
- Monitor access to environment files
|
|
||||||
|
|
||||||
### ❌ **Don'ts**
|
|
||||||
|
|
||||||
- Never commit `.env` files to version control
|
|
||||||
- Don't share `.env` files via insecure channels
|
|
||||||
- Don't use default passwords in production
|
|
||||||
- Don't leave `.env.backup` files on the system after recording credentials
|
|
||||||
|
|
||||||
## Recovery
|
|
||||||
|
|
||||||
### **Lost Credentials**
|
|
||||||
|
|
||||||
If you lose your database credentials:
|
|
||||||
|
|
||||||
1. Check if `.env.backup` file exists:
|
|
||||||
```bash
|
|
||||||
sudo cat /usr/local/CyberCP/.env.backup
|
|
||||||
```
|
|
||||||
|
|
||||||
2. If backup doesn't exist, you'll need to reset MySQL passwords using MySQL recovery procedures
|
|
||||||
|
|
||||||
### **Regenerate Environment**
|
|
||||||
|
|
||||||
To regenerate environment configuration:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /usr/local/CyberCP
|
|
||||||
sudo python install/env_generator.py /usr/local/CyberCP
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration Options
|
|
||||||
|
|
||||||
### **Environment Variables**
|
|
||||||
|
|
||||||
| Variable | Description | Default |
|
|
||||||
|----------|-------------|---------|
|
|
||||||
| `SECRET_KEY` | Django secret key | Generated (64 chars) |
|
|
||||||
| `DB_PASSWORD` | CyberPanel DB password | Generated (24 chars) |
|
|
||||||
| `ROOT_DB_PASSWORD` | MySQL root password | Generated (24 chars) |
|
|
||||||
| `DEBUG` | Debug mode | False |
|
|
||||||
| `ALLOWED_HOSTS` | Allowed hosts | localhost,127.0.0.1,hostname |
|
|
||||||
|
|
||||||
### **Custom Configuration**
|
|
||||||
|
|
||||||
To use custom passwords during installation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python install/env_generator.py /usr/local/CyberCP "your_root_password" "your_db_password"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### **Installation Fails**
|
|
||||||
|
|
||||||
If the new secure installation fails:
|
|
||||||
|
|
||||||
1. Check installation logs for error messages
|
|
||||||
2. The system will automatically fallback to the original installation method
|
|
||||||
3. Verify Python dependencies are installed:
|
|
||||||
```bash
|
|
||||||
pip install python-dotenv
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Environment Loading Issues**
|
|
||||||
|
|
||||||
If Django can't load environment variables:
|
|
||||||
|
|
||||||
1. Ensure `.env` file exists and has correct permissions:
|
|
||||||
```bash
|
|
||||||
ls -la /usr/local/CyberCP/.env
|
|
||||||
# Should show: -rw------- 1 root root
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Install python-dotenv if missing:
|
|
||||||
```bash
|
|
||||||
pip install python-dotenv
|
|
||||||
```
|
|
||||||
|
|
||||||
## Migration from Old Installation
|
|
||||||
|
|
||||||
### **Existing Installations**
|
|
||||||
|
|
||||||
For existing CyberPanel installations with hardcoded passwords:
|
|
||||||
|
|
||||||
1. **Backup current configuration**:
|
|
||||||
```bash
|
|
||||||
cp /usr/local/CyberCP/CyberCP/settings.py /usr/local/CyberCP/CyberCP/settings.py.backup
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Generate new environment configuration**:
|
|
||||||
```bash
|
|
||||||
cd /usr/local/CyberCP
|
|
||||||
python install/env_generator.py /usr/local/CyberCP
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Update settings.py** (already done in new installations):
|
|
||||||
- The settings.py file now supports environment variables
|
|
||||||
- It will fallback to hardcoded values if .env is not available
|
|
||||||
|
|
||||||
4. **Test the configuration**:
|
|
||||||
```bash
|
|
||||||
cd /usr/local/CyberCP
|
|
||||||
python manage.py check
|
|
||||||
```
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
For issues with the secure installation:
|
|
||||||
|
|
||||||
1. Check the installation logs
|
|
||||||
2. Verify file permissions
|
|
||||||
3. Ensure all dependencies are installed
|
|
||||||
4. Review the fallback installation method if needed
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Security Notice**: This installation method significantly improves security by eliminating hardcoded credentials. Always ensure proper file permissions and secure handling of environment files.
|
|
||||||
Reference in New Issue
Block a user