mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-10-26 00:36:34 +02:00
Initial commit for v2.4.3
This commit is contained in:
4609
.idea/workspace.xml
generated
Normal file
4609
.idea/workspace.xml
generated
Normal file
File diff suppressed because it is too large
Load Diff
257
AIScannerDocs.md
Normal file
257
AIScannerDocs.md
Normal file
@@ -0,0 +1,257 @@
|
||||
# CyberPanel AI Security Scanner Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The CyberPanel AI Security Scanner is an advanced security tool that uses artificial intelligence to scan WordPress websites for vulnerabilities, malware, and security threats. It provides comprehensive security analysis with detailed findings and recommendations.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Features](#features)
|
||||
2. [Getting Started](#getting-started)
|
||||
3. [Configuration](#configuration)
|
||||
4. [Running Scans](#running-scans)
|
||||
5. [Understanding Scan Results](#understanding-scan-results)
|
||||
6. [VPS Free Scans](#vps-free-scans)
|
||||
7. [API Integration](#api-integration)
|
||||
8. [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Features
|
||||
|
||||
- **AI-Powered Analysis**: Uses advanced AI models to detect security threats and vulnerabilities
|
||||
- **Real-time Scanning**: Monitor scan progress in real-time with live updates
|
||||
- **Comprehensive Coverage**: Scans WordPress core files, themes, plugins, and custom code
|
||||
- **Multiple Scan Types**: Choose between quick scans or full comprehensive scans
|
||||
- **Detailed Reports**: Get detailed findings with severity levels and remediation suggestions
|
||||
- **Platform Integration**: View detailed analysis on the CyberPersons platform
|
||||
- **VPS Free Scans**: Eligible VPS customers get free security scans
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- CyberPanel v2.4.2 or higher
|
||||
- WordPress website(s) hosted on your server
|
||||
- Active internet connection for API communication
|
||||
|
||||
### Initial Setup
|
||||
|
||||
1. **Access AI Scanner**
|
||||
- Log in to your CyberPanel admin panel
|
||||
- Navigate to **Security** → **AI Security Scanner**
|
||||
|
||||
2. **Configure Payment Method** (Skip if using VPS free scans)
|
||||
- Click on **Setup Payment Method**
|
||||
- You'll be redirected to the CyberPersons platform
|
||||
- Complete the payment setup process
|
||||
- Return to CyberPanel after completion
|
||||
|
||||
3. **Verify Setup**
|
||||
- Your account balance will be displayed
|
||||
- API key will be automatically configured
|
||||
|
||||
## Configuration
|
||||
|
||||
### Payment Methods
|
||||
|
||||
The AI Scanner uses a pay-per-scan model. You can:
|
||||
|
||||
1. **Add Payment Method**
|
||||
- Click **Add Payment Method** button
|
||||
- Complete the setup on the platform
|
||||
- Multiple payment methods supported
|
||||
|
||||
2. **Check Balance**
|
||||
- Current balance displayed on main page
|
||||
- Click **Refresh Balance** to update
|
||||
|
||||
### User Permissions
|
||||
|
||||
- **Admin Users**: Full access to all scans and settings
|
||||
- **Reseller Users**: Can scan their own websites and view their scan history
|
||||
- **Regular Users**: Can only scan websites they own
|
||||
|
||||
## Running Scans
|
||||
|
||||
### Starting a New Scan
|
||||
|
||||
1. **Select Website**
|
||||
- Choose the WordPress website to scan from the dropdown
|
||||
- Only websites you have access to will be shown
|
||||
|
||||
2. **Choose Scan Type**
|
||||
- **Quick Scan**: Faster scan focusing on critical areas
|
||||
- **Full Scan**: Comprehensive scan of all files
|
||||
|
||||
3. **Start Scan**
|
||||
- Click **Start Scan** button
|
||||
- Scan will begin immediately
|
||||
|
||||
### Monitoring Progress
|
||||
|
||||
During the scan, you can see:
|
||||
- Current scan phase (Discovering files, Scanning, Analyzing)
|
||||
- Progress percentage
|
||||
- Files discovered and scanned
|
||||
- Threats found in real-time
|
||||
- Current file being analyzed
|
||||
|
||||
### Scan Phases
|
||||
|
||||
1. **Starting**: Initializing scan and setting up access
|
||||
2. **Discovering Files**: Mapping website structure
|
||||
3. **Scanning Files**: Analyzing files for threats
|
||||
4. **Completing**: Finalizing analysis and generating report
|
||||
5. **Completed**: Scan finished, results available
|
||||
|
||||
## Understanding Scan Results
|
||||
|
||||
### Scan Summary
|
||||
|
||||
Each completed scan shows:
|
||||
- **Domain**: Website that was scanned
|
||||
- **Scan Type**: Quick or Full scan
|
||||
- **Duration**: Time taken to complete
|
||||
- **Files Scanned**: Total number of files analyzed
|
||||
- **Threats Found**: Number of security issues detected
|
||||
- **Cost**: Scan cost (if applicable)
|
||||
|
||||
### Threat Levels
|
||||
|
||||
Threats are categorized by severity:
|
||||
- **CRITICAL**: Immediate action required
|
||||
- **HIGH**: Serious issues that should be addressed soon
|
||||
- **MEDIUM**: Moderate risks that need attention
|
||||
- **LOW**: Minor issues or recommendations
|
||||
|
||||
### Viewing Detailed Results
|
||||
|
||||
1. **In CyberPanel**
|
||||
- Click on a scan in the history table
|
||||
- View summary and key findings
|
||||
|
||||
2. **On Platform** (Detailed Analysis)
|
||||
- Click **View on Platform** button
|
||||
- Opens detailed AI analysis in new tab
|
||||
- Includes code snippets, explanations, and fixes
|
||||
|
||||
## VPS Free Scans
|
||||
|
||||
### Eligibility
|
||||
|
||||
VPS customers hosted with participating providers receive free security scans:
|
||||
- Automatic detection based on server IP
|
||||
- No payment setup required
|
||||
- Limited number of free scans per month
|
||||
|
||||
### Using Free Scans
|
||||
|
||||
1. System automatically detects VPS eligibility
|
||||
2. Free scans available immediately
|
||||
3. Remaining free scans shown when starting scan
|
||||
4. After free scans exhausted, payment required
|
||||
|
||||
### Security
|
||||
|
||||
- Time-limited access tokens
|
||||
- Scan-specific permissions
|
||||
- Automatic token expiration
|
||||
- IP-based restrictions
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **"Payment not configured" Error**
|
||||
- Complete payment setup process
|
||||
- Verify API key is saved
|
||||
- Check account balance
|
||||
|
||||
2. **"API key not configured" Error**
|
||||
- Ensure payment setup completed
|
||||
- For VPS users, check eligibility
|
||||
- Try refreshing the page
|
||||
|
||||
3. **Scan Stuck in "Running" State**
|
||||
- Wait 5-10 minutes for completion
|
||||
- Check scan status on platform
|
||||
- Contact support if persists
|
||||
|
||||
4. **"Access Denied" Errors**
|
||||
- Verify you own the website
|
||||
- Check user permissions
|
||||
- Ensure website exists
|
||||
|
||||
### Getting Help
|
||||
|
||||
1. **Check Logs**
|
||||
```bash
|
||||
tail -f /home/cyberpanel/error-logs.txt | grep "AI Scanner"
|
||||
```
|
||||
|
||||
2. **Support Channels**
|
||||
- CyberPanel Forums: https://community.cyberpanel.net
|
||||
- Support Ticket: https://platform.cyberpersons.com
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Regular Scanning** (Upcoming feature)
|
||||
- Schedule weekly or monthly scans
|
||||
- Scan after major updates
|
||||
- Scan new installations
|
||||
|
||||
2. **Act on Findings**
|
||||
- Address CRITICAL issues immediately
|
||||
- Plan remediation for HIGH issues
|
||||
- Review all recommendations
|
||||
|
||||
3. **Security Hygiene**
|
||||
- Keep WordPress updated
|
||||
- Remove unused plugins/themes
|
||||
- Use strong passwords
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Command Line Interface
|
||||
|
||||
For automated scanning:
|
||||
|
||||
```python
|
||||
# Example using CyberPanel API
|
||||
import requests
|
||||
|
||||
# Start a scan
|
||||
response = requests.post(
|
||||
'https://your-server:8090/aiscanner/start-scan/',
|
||||
json={
|
||||
'domain': 'example.com',
|
||||
'scan_type': 'full'
|
||||
},
|
||||
headers={'Authorization': 'your-api-key'}
|
||||
)
|
||||
```
|
||||
|
||||
### Integration with CI/CD
|
||||
|
||||
Include security scanning in your deployment pipeline:
|
||||
|
||||
1. Trigger scan after deployment
|
||||
2. Wait for completion webhook
|
||||
3. Fail pipeline if critical issues found
|
||||
|
||||
## Changelog
|
||||
|
||||
### Version 2.4.2
|
||||
- Added platform monitor URL integration
|
||||
- Support for VPS free scans
|
||||
- Improved error handling
|
||||
- Enhanced scan progress tracking
|
||||
|
||||
### Version 2.4.1
|
||||
- Initial AI Scanner release
|
||||
- WordPress security scanning
|
||||
- Real-time progress updates
|
||||
- Payment integration
|
||||
|
||||
---
|
||||
|
||||
**Note**: This documentation is for CyberPanel AI Security Scanner. For platform-specific features, refer to the [CyberPersons Platform Documentation](https://platform.cyberpersons.com/).
|
||||
2984
AllCPUbuntu.json
Normal file
2984
AllCPUbuntu.json
Normal file
File diff suppressed because it is too large
Load Diff
360
ApachController/ApacheController.py
Normal file
360
ApachController/ApacheController.py
Normal file
@@ -0,0 +1,360 @@
|
||||
#!/usr/local/CyberCP/bin/python
|
||||
import os
|
||||
import subprocess
|
||||
import shlex
|
||||
import plogical.CyberCPLogFileWriter as logging
|
||||
from ApachController.ApacheVhosts import ApacheVhost
|
||||
from plogical.processUtilities import ProcessUtilities
|
||||
|
||||
|
||||
class ApacheController:
|
||||
apacheInstallStatusPath = '/home/cyberpanel/apacheInstallStatus'
|
||||
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
|
||||
|
||||
serverRootPath = '/etc/httpd'
|
||||
configBasePath = '/etc/httpd/conf.d/'
|
||||
phpBasepath = '/etc/opt/remi'
|
||||
php54Path = '/opt/remi/php54/root/etc/php-fpm.d/'
|
||||
php55Path = '/opt/remi/php55/root/etc/php-fpm.d/'
|
||||
php56Path = '/etc/opt/remi/php56/php-fpm.d/'
|
||||
php70Path = '/etc/opt/remi/php70/php-fpm.d/'
|
||||
php71Path = '/etc/opt/remi/php71/php-fpm.d/'
|
||||
php72Path = '/etc/opt/remi/php72/php-fpm.d/'
|
||||
php73Path = '/etc/opt/remi/php73/php-fpm.d/'
|
||||
|
||||
php74Path = '/etc/opt/remi/php74/php-fpm.d/'
|
||||
php80Path = '/etc/opt/remi/php80/php-fpm.d/'
|
||||
php81Path = '/etc/opt/remi/php81/php-fpm.d/'
|
||||
php82Path = '/etc/opt/remi/php82/php-fpm.d/'
|
||||
php83Path = '/etc/opt/remi/php83/php-fpm.d/'
|
||||
php84Path = '/etc/opt/remi/php84/php-fpm.d/'
|
||||
php85Path = '/etc/opt/remi/php85/php-fpm.d/'
|
||||
|
||||
serviceName = 'httpd'
|
||||
|
||||
else:
|
||||
serverRootPath = '/etc/apache2'
|
||||
configBasePath = '/etc/apache2/sites-enabled/'
|
||||
|
||||
phpBasepath = '/etc/php'
|
||||
|
||||
php54Path = '/etc/php/5.4/fpm/pool.d/'
|
||||
php55Path = '/etc/php/5.5/fpm/pool.d/'
|
||||
php56Path = '/etc/php/5.6/fpm/pool.d/'
|
||||
php70Path = '/etc/php/7.0/fpm/pool.d/'
|
||||
php71Path = '/etc/php/7.1/fpm/pool.d/'
|
||||
php72Path = '/etc/php/7.2/fpm/pool.d/'
|
||||
php73Path = '/etc/php/7.3/fpm/pool.d/'
|
||||
php74Path = '/etc/php/7.4/fpm/pool.d/'
|
||||
|
||||
php80Path = '/etc/php/8.0/fpm/pool.d/'
|
||||
php81Path = '/etc/php/8.1/fpm/pool.d/'
|
||||
php82Path = '/etc/php/8.2/fpm/pool.d/'
|
||||
|
||||
php83Path = '/etc/php/8.3/fpm/pool.d/'
|
||||
php84Path = '/etc/php/8.4/fpm/pool.d/'
|
||||
php85Path = '/etc/php/8.5/fpm/pool.d/'
|
||||
|
||||
serviceName = 'apache2'
|
||||
|
||||
mpmConfigs = """# Select the MPM module which should be used by uncommenting exactly
|
||||
# one of the following LoadModule lines:
|
||||
|
||||
# prefork MPM: Implements a non-threaded, pre-forking web server
|
||||
# See: http://httpd.apache.org/docs/2.4/mod/prefork.html
|
||||
#LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
|
||||
|
||||
# worker MPM: Multi-Processing Module implementing a hybrid
|
||||
# multi-threaded multi-process web server
|
||||
# See: http://httpd.apache.org/docs/2.4/mod/worker.html
|
||||
#
|
||||
#LoadModule mpm_worker_module modules/mod_mpm_worker.so
|
||||
|
||||
# event MPM: A variant of the worker MPM with the goal of consuming
|
||||
# threads only for connections with active processing
|
||||
# See: http://httpd.apache.org/docs/2.4/mod/event.html
|
||||
#
|
||||
LoadModule mpm_event_module modules/mod_mpm_event.so
|
||||
|
||||
<IfModule mpm_event_module>
|
||||
StartServers 2
|
||||
MinSpareThreads 25
|
||||
MaxSpareThreads 75
|
||||
ThreadLimit 64
|
||||
ThreadsPerChild 25
|
||||
MaxRequestWorkers 30
|
||||
MaxConnectionsPerChild 1000
|
||||
</IfModule>"""
|
||||
mpmConfigsPath = "/etc/httpd/conf.modules.d/00-mpm.conf"
|
||||
|
||||
@staticmethod
|
||||
def checkIfApacheInstalled():
|
||||
try:
|
||||
if os.path.exists(ApacheController.php80Path):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
# if os.path.exists(ApacheVhost.php54Path):
|
||||
# pass
|
||||
# else:
|
||||
# return 0
|
||||
#
|
||||
# if os.path.exists(ApacheVhost.php55Path):
|
||||
# pass
|
||||
# else:
|
||||
# return 0
|
||||
#
|
||||
# if os.path.exists(ApacheVhost.php56Path):
|
||||
# pass
|
||||
# else:
|
||||
# return 0
|
||||
#
|
||||
# if os.path.exists(ApacheVhost.php70Path):
|
||||
# pass
|
||||
# else:
|
||||
# return 0
|
||||
#
|
||||
# if os.path.exists(ApacheVhost.php71Path):
|
||||
# pass
|
||||
# else:
|
||||
# return 0
|
||||
#
|
||||
# if os.path.exists(ApacheVhost.php72Path):
|
||||
# pass
|
||||
# else:
|
||||
# return 0
|
||||
#
|
||||
# if os.path.exists(ApacheVhost.php73Path):
|
||||
# return 1
|
||||
# else:
|
||||
# return 0
|
||||
|
||||
|
||||
except BaseException as msg:
|
||||
message = "%s. [%s]" % (str(msg), '[ApacheController.checkIfApacheInstalled]')
|
||||
logging.CyberCPLogFileWriter.writeToFile(message)
|
||||
|
||||
@staticmethod
|
||||
def executioner(command):
|
||||
try:
|
||||
# subprocess.call(shlex.split(command))
|
||||
res = subprocess.call(shlex.split(command))
|
||||
if res == 1:
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
except BaseException as msg:
|
||||
logging.CyberCPLogFileWriter.writeToFile(str(msg))
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def InstallApache():
|
||||
try:
|
||||
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
command = "yum install -y httpd httpd-tools mod_ssl php-fpm"
|
||||
else:
|
||||
command = "apt update -y && sudo apt upgrade -y && apt install apache2 -y"
|
||||
|
||||
if ProcessUtilities.executioner(command, None, True) == 0:
|
||||
return "Failed to install Apache and PHP-FPM."
|
||||
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
|
||||
# command = "yum -y install centos-release-scl yum-utils"
|
||||
# if ProcessUtilities.executioner(command) == 0:
|
||||
# return "Failed to centos-release-scl and yum-utils"
|
||||
#
|
||||
# command = "yum-config-manager --enable rhel-server-rhscl-7-rpms"
|
||||
# if ProcessUtilities.executioner(command) == 0:
|
||||
# return "Failed to --enable rhel-server-rhscl-7-rpms"
|
||||
|
||||
sslPath = "/etc/httpd/conf.d/ssl.conf"
|
||||
|
||||
if os.path.exists(sslPath):
|
||||
os.remove(sslPath)
|
||||
|
||||
confPath = ApacheVhost.serverRootPath + "/conf/httpd.conf"
|
||||
|
||||
CurrentConf = open(confPath, 'r').read()
|
||||
|
||||
if CurrentConf.find('Listen 8083') == -1:
|
||||
|
||||
data = open(confPath, 'r').readlines()
|
||||
writeToFile = open(confPath, 'w')
|
||||
|
||||
for items in data:
|
||||
if items.find("Listen") > -1 and items.find("80") > -1 and items.find('#') == -1:
|
||||
writeToFile.writelines("Listen 8083\nListen 8082\n")
|
||||
elif items.find("User") > -1 and items.find('#') == -1:
|
||||
writeToFile.writelines("User nobody\n")
|
||||
elif items.find("Group") > -1 and items.find('#') == -1:
|
||||
writeToFile.writelines("Group nobody\n")
|
||||
writeToFile.writelines('SetEnv LSWS_EDITION Openlitespeed\nSetEnv X-LSCACHE on\n')
|
||||
elif items[0] == "#":
|
||||
continue
|
||||
else:
|
||||
writeToFile.writelines(items)
|
||||
|
||||
writeToFile.close()
|
||||
|
||||
# MPM Module Configurations
|
||||
|
||||
writeToFile = open(ApacheController.mpmConfigsPath, 'w')
|
||||
writeToFile.write(ApacheController.mpmConfigs)
|
||||
writeToFile.close()
|
||||
|
||||
|
||||
else:
|
||||
|
||||
confPath = ApacheVhost.serverRootPath + "/apache2.conf"
|
||||
|
||||
portsPath = '/etc/apache2/ports.conf'
|
||||
|
||||
WriteToFile = open(portsPath, 'w')
|
||||
WriteToFile.write('Listen 8083\nListen 8082\n')
|
||||
WriteToFile.close()
|
||||
|
||||
|
||||
|
||||
command = f"sed -i 's/User ${{APACHE_RUN_USER}}/User nobody/g' {confPath}"
|
||||
if ProcessUtilities.executioner(command, None, True) == 0:
|
||||
return "Apache run user change failed"
|
||||
|
||||
command = f"sed -i 's/Group ${{APACHE_RUN_GROUP}}/Group nogroup/g' {confPath}"
|
||||
if ProcessUtilities.executioner(command, None, True) == 0:
|
||||
return "Apache run group change failed"
|
||||
|
||||
command = 'apt-get install apache2-suexec-pristine -y'
|
||||
if ProcessUtilities.executioner(command, None, True) == 0:
|
||||
return "Apache run apache2-suexec-pristine"
|
||||
|
||||
command = 'a2enmod suexec proxy ssl proxy_fcgi proxy rewrite headers'
|
||||
if ProcessUtilities.executioner(command, None, True) == 0:
|
||||
return "Apache run suexec proxy ssl"
|
||||
|
||||
|
||||
WriteToFile = open(confPath, 'a')
|
||||
WriteToFile.writelines('\nSetEnv LSWS_EDITION Openlitespeed\nSetEnv X-LSCACHE on\n')
|
||||
WriteToFile.close()
|
||||
|
||||
###
|
||||
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
serviceName = 'httpd'
|
||||
else:
|
||||
serviceName = 'apache2'
|
||||
|
||||
command = f"systemctl start {serviceName}.service"
|
||||
ApacheController.executioner(command)
|
||||
command = f"systemctl enable {serviceName}.service"
|
||||
ApacheController.executioner(command)
|
||||
|
||||
return 1
|
||||
|
||||
except BaseException as msg:
|
||||
return str(msg)
|
||||
|
||||
@staticmethod
|
||||
def phpVersions():
|
||||
# Version 5.4
|
||||
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
if ProcessUtilities.alma9check == 1:
|
||||
command = 'yum install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm'
|
||||
else:
|
||||
command = 'yum install -y https://rpms.remirepo.net/enterprise/remi-release-8.rpm'
|
||||
ApacheController.executioner(command)
|
||||
|
||||
command = "yum install -y php?? php??-php-fpm php??-php-mysql php??-php-curl php??-php-gd php??-php-mbstring php??-php-xml php??-php-zip php??-php-intl"
|
||||
if ProcessUtilities.executioner(command, None, True) == 0:
|
||||
return "Failed to install php54-fpm"
|
||||
|
||||
|
||||
else:
|
||||
|
||||
command = 'apt-get install software-properties-common -y'
|
||||
if ProcessUtilities.executioner(command, None, True) == 0:
|
||||
return "Failed to install software-properties-common"
|
||||
|
||||
command = 'apt install python-software-properties -y'
|
||||
if ProcessUtilities.executioner(command, None, True) == 0:
|
||||
return "Failed to install python-software-properties"
|
||||
|
||||
command = 'add-apt-repository ppa:ondrej/php -y'
|
||||
if ProcessUtilities.executioner(command, None, True) == 0:
|
||||
return "Failed to ppa:ondrej/php"
|
||||
|
||||
command = "DEBIAN_FRONTEND=noninteractive apt-get install -y php-fpm php?.?-fpm php?.?-fpm php?.?-mysql php?.?-curl php?.?-gd php?.?-mbstring php?.?-xml php?.?-zip php?.?-intl"
|
||||
|
||||
if ProcessUtilities.executioner(command, None, True) == 0:
|
||||
return "Failed to install Apache and PHP-FPM."
|
||||
|
||||
from plogical.upgrade import Upgrade
|
||||
Upgrade.CreateMissingPoolsforFPM()
|
||||
|
||||
# try:
|
||||
# wwwConfPath = ApacheVhost.php54Path + "/www.conf"
|
||||
#
|
||||
# if os.path.exists(wwwConfPath):
|
||||
# os.remove(wwwConfPath)
|
||||
#
|
||||
# wwwConfPath = ApacheVhost.php55Path + "/www.conf"
|
||||
#
|
||||
# if os.path.exists(wwwConfPath):
|
||||
# os.remove(wwwConfPath)
|
||||
#
|
||||
# wwwConfPath = ApacheVhost.php56Path + "/www.conf"
|
||||
#
|
||||
# if os.path.exists(wwwConfPath):
|
||||
# os.remove(wwwConfPath)
|
||||
#
|
||||
# wwwConfPath = ApacheVhost.php70Path + "/www.conf"
|
||||
#
|
||||
# if os.path.exists(wwwConfPath):
|
||||
# os.remove(wwwConfPath)
|
||||
#
|
||||
# wwwConfPath = ApacheVhost.php71Path + "/www.conf"
|
||||
#
|
||||
# if os.path.exists(wwwConfPath):
|
||||
# os.remove(wwwConfPath)
|
||||
#
|
||||
# wwwConfPath = ApacheVhost.php72Path + "/www.conf"
|
||||
#
|
||||
# if os.path.exists(wwwConfPath):
|
||||
# os.remove(wwwConfPath)
|
||||
#
|
||||
# wwwConfPath = ApacheVhost.php73Path + "/www.conf"
|
||||
#
|
||||
# if os.path.exists(wwwConfPath):
|
||||
# os.remove(wwwConfPath)
|
||||
# except:
|
||||
# pass
|
||||
|
||||
return 1
|
||||
|
||||
@staticmethod
|
||||
def setupApache(statusFile):
|
||||
try:
|
||||
|
||||
logging.CyberCPLogFileWriter.statusWriter(statusFile, 'Starting Apache installation. It may take some time..,70')
|
||||
|
||||
result = ApacheController.InstallApache()
|
||||
|
||||
if result != 1:
|
||||
return [0,result]
|
||||
|
||||
logging.CyberCPLogFileWriter.statusWriter(statusFile,
|
||||
'Installing PHP-FPM Versions. It may take some time..,80')
|
||||
|
||||
result = ApacheController.phpVersions()
|
||||
|
||||
if result != 1:
|
||||
return [0,result]
|
||||
|
||||
return [1, 'None']
|
||||
except BaseException as msg:
|
||||
return [0, str(msg)]
|
||||
679
ApachController/ApacheVhosts.py
Normal file
679
ApachController/ApacheVhosts.py
Normal file
@@ -0,0 +1,679 @@
|
||||
#!/usr/local/CyberCP/bin/python
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
import django
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
|
||||
django.setup()
|
||||
import os
|
||||
from websiteFunctions.models import Websites, ChildDomains
|
||||
from plogical.vhostConfs import vhostConfs
|
||||
from managePHP.phpManager import PHPManager
|
||||
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
|
||||
from plogical.processUtilities import ProcessUtilities
|
||||
import re
|
||||
|
||||
class ApacheVhost:
|
||||
apacheInstallStatusPath = '/home/cyberpanel/apacheInstallStatus'
|
||||
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
|
||||
serverRootPath = '/etc/httpd'
|
||||
configBasePath = '/etc/httpd/conf.d/'
|
||||
php54Path = '/opt/remi/php54/root/etc/php-fpm.d/'
|
||||
php55Path = '/opt/remi/php55/root/etc/php-fpm.d/'
|
||||
php56Path = '/etc/opt/remi/php56/php-fpm.d/'
|
||||
php70Path = '/etc/opt/remi/php70/php-fpm.d/'
|
||||
php71Path = '/etc/opt/remi/php71/php-fpm.d/'
|
||||
php72Path = '/etc/opt/remi/php72/php-fpm.d/'
|
||||
php73Path = '/etc/opt/remi/php73/php-fpm.d/'
|
||||
|
||||
php74Path = '/etc/opt/remi/php74/php-fpm.d/'
|
||||
php80Path = '/etc/opt/remi/php80/php-fpm.d/'
|
||||
php81Path = '/etc/opt/remi/php81/php-fpm.d/'
|
||||
php82Path = '/etc/opt/remi/php82/php-fpm.d/'
|
||||
php83Path = '/etc/opt/remi/php83/php-fpm.d/'
|
||||
php84Path = '/etc/opt/remi/php84/php-fpm.d/'
|
||||
php85Path = '/etc/opt/remi/php85/php-fpm.d/'
|
||||
|
||||
serviceName = 'httpd'
|
||||
|
||||
else:
|
||||
serverRootPath = '/etc/apache2'
|
||||
configBasePath = '/etc/apache2/sites-enabled/'
|
||||
|
||||
php54Path = '/etc/php/5.4/fpm/pool.d/'
|
||||
php55Path = '/etc/php/5.5/fpm/pool.d/'
|
||||
php56Path = '/etc/php/5.6/fpm/pool.d/'
|
||||
php70Path = '/etc/php/7.0/fpm/pool.d/'
|
||||
php71Path = '/etc/php/7.1/fpm/pool.d/'
|
||||
php72Path = '/etc/php/7.2/fpm/pool.d/'
|
||||
php73Path = '/etc/php/7.3/fpm/pool.d/'
|
||||
php74Path = '/etc/php/7.4/fpm/pool.d/'
|
||||
|
||||
php80Path = '/etc/php/8.0/fpm/pool.d/'
|
||||
php81Path = '/etc/php/8.1/fpm/pool.d/'
|
||||
php82Path = '/etc/php/8.2/fpm/pool.d/'
|
||||
php83Path = '/etc/php/8.3/fpm/pool.d/'
|
||||
php84Path = '/etc/php/8.4/fpm/pool.d/'
|
||||
php85Path = '/etc/php/8.5/fpm/pool.d/'
|
||||
|
||||
serviceName = 'apache2'
|
||||
|
||||
|
||||
|
||||
lswsMainConf = "/usr/local/lsws/conf/httpd_config.conf"
|
||||
|
||||
count = 0
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
sslBasePath = "/etc/httpd/conf.d/ssl/"
|
||||
else:
|
||||
sslBasePath = "/etc/apache2/conf-enabled/"
|
||||
|
||||
@staticmethod
|
||||
def DecidePHPPath(php, virtualHostName):
|
||||
if php == '53' or php == '54':
|
||||
finalConfPath = ApacheVhost.php54Path + virtualHostName
|
||||
elif php == '55':
|
||||
finalConfPath = ApacheVhost.php55Path + virtualHostName
|
||||
elif php == '56':
|
||||
finalConfPath = ApacheVhost.php56Path + virtualHostName
|
||||
elif php == '70':
|
||||
finalConfPath = ApacheVhost.php70Path + virtualHostName
|
||||
elif php == '71':
|
||||
finalConfPath = ApacheVhost.php71Path + virtualHostName
|
||||
elif php == '72':
|
||||
finalConfPath = ApacheVhost.php72Path + virtualHostName
|
||||
elif php == '73':
|
||||
finalConfPath = ApacheVhost.php73Path + virtualHostName
|
||||
elif php == '74':
|
||||
finalConfPath = ApacheVhost.php74Path + virtualHostName
|
||||
elif php == '80':
|
||||
finalConfPath = ApacheVhost.php80Path + virtualHostName
|
||||
elif php == '81':
|
||||
finalConfPath = ApacheVhost.php81Path + virtualHostName
|
||||
elif php == '82':
|
||||
finalConfPath = ApacheVhost.php82Path + virtualHostName
|
||||
elif php == '83':
|
||||
finalConfPath = ApacheVhost.php83Path + virtualHostName
|
||||
elif php == '84':
|
||||
finalConfPath = ApacheVhost.php84Path + virtualHostName
|
||||
elif php == '85':
|
||||
finalConfPath = ApacheVhost.php85Path + virtualHostName
|
||||
|
||||
if os.path.exists(ProcessUtilities.debugPath):
|
||||
logging.writeToFile(f'Decided path in DecidePHPPath {finalConfPath}.conf')
|
||||
|
||||
return finalConfPath + '.conf'
|
||||
|
||||
@staticmethod
|
||||
def whichPHPExists(virtualHostName):
|
||||
|
||||
virtualHostName = virtualHostName + ".conf"
|
||||
|
||||
if os.path.exists(ApacheVhost.php54Path + virtualHostName):
|
||||
return ApacheVhost.php54Path + virtualHostName
|
||||
|
||||
if os.path.exists(ApacheVhost.php55Path + virtualHostName):
|
||||
return ApacheVhost.php55Path + virtualHostName
|
||||
|
||||
if os.path.exists(ApacheVhost.php56Path + virtualHostName):
|
||||
return ApacheVhost.php56Path + virtualHostName
|
||||
|
||||
if os.path.exists(ApacheVhost.php70Path + virtualHostName):
|
||||
return ApacheVhost.php70Path + virtualHostName
|
||||
|
||||
if os.path.exists(ApacheVhost.php71Path + virtualHostName):
|
||||
return ApacheVhost.php71Path + virtualHostName
|
||||
|
||||
if os.path.exists(ApacheVhost.php72Path + virtualHostName):
|
||||
return ApacheVhost.php72Path + virtualHostName
|
||||
|
||||
if os.path.exists(ApacheVhost.php73Path + virtualHostName):
|
||||
return ApacheVhost.php73Path + virtualHostName
|
||||
|
||||
if os.path.exists(ApacheVhost.php74Path + virtualHostName):
|
||||
return ApacheVhost.php74Path + virtualHostName
|
||||
|
||||
if os.path.exists(ApacheVhost.php80Path + virtualHostName):
|
||||
return ApacheVhost.php80Path + virtualHostName
|
||||
|
||||
if os.path.exists(ApacheVhost.php81Path + virtualHostName):
|
||||
return ApacheVhost.php81Path + virtualHostName
|
||||
|
||||
if os.path.exists(ApacheVhost.php82Path + virtualHostName):
|
||||
return ApacheVhost.php82Path + virtualHostName
|
||||
|
||||
if os.path.exists(ApacheVhost.php83Path + virtualHostName):
|
||||
return ApacheVhost.php83Path + virtualHostName
|
||||
|
||||
if os.path.exists(ApacheVhost.php84Path + virtualHostName):
|
||||
return ApacheVhost.php84Path + virtualHostName
|
||||
|
||||
if os.path.exists(ApacheVhost.php85Path + virtualHostName):
|
||||
return ApacheVhost.php85Path + virtualHostName
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def GenerateSelfSignedSSL(virtualHostName):
|
||||
if os.path.exists(ApacheVhost.sslBasePath):
|
||||
pass
|
||||
else:
|
||||
os.mkdir(ApacheVhost.sslBasePath)
|
||||
|
||||
pathToStoreSSLPrivKey = ApacheVhost.sslBasePath + ".privkey.pem"
|
||||
pathToStoreSSLFullChain = ApacheVhost.sslBasePath + ".fullchain.pem"
|
||||
command = 'openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout ' + pathToStoreSSLPrivKey + ' -out ' + pathToStoreSSLFullChain
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
@staticmethod
|
||||
def perHostVirtualConf(administratorEmail,externalApp, virtualHostUser, phpVersion, virtualHostName):
|
||||
try:
|
||||
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
sockPath = '/var/run/php-fpm/'
|
||||
group = 'nobody'
|
||||
else:
|
||||
sockPath = '/var/run/php/'
|
||||
group = 'nogroup'
|
||||
|
||||
## Non-SSL Conf
|
||||
|
||||
finalConfPath = ApacheVhost.configBasePath + virtualHostName + '.conf'
|
||||
confFile = open(finalConfPath, "w+")
|
||||
|
||||
php = PHPManager.getPHPString(phpVersion)
|
||||
|
||||
currentConf = vhostConfs.apacheConf
|
||||
currentConf = currentConf.replace('{virtualHostName}', virtualHostName)
|
||||
currentConf = currentConf.replace('{administratorEmail}', administratorEmail)
|
||||
currentConf = currentConf.replace('{virtualHostUser}', virtualHostUser)
|
||||
currentConf = currentConf.replace('{php}', php)
|
||||
currentConf = currentConf.replace('{adminEmails}', administratorEmail)
|
||||
currentConf = currentConf.replace('{externalApp}', virtualHostUser)
|
||||
currentConf = currentConf.replace('{sockPath}', sockPath)
|
||||
|
||||
confFile.write(currentConf)
|
||||
confFile.close()
|
||||
|
||||
## SSL Conf
|
||||
|
||||
finalConfPath = ApacheVhost.configBasePath + virtualHostName + '.conf'
|
||||
confFile = open(finalConfPath, "a")
|
||||
|
||||
php = PHPManager.getPHPString(phpVersion)
|
||||
|
||||
currentConf = vhostConfs.apacheConfSSL
|
||||
currentConf = currentConf.replace('{virtualHostName}', virtualHostName)
|
||||
currentConf = currentConf.replace('{administratorEmail}', administratorEmail)
|
||||
currentConf = currentConf.replace('{virtualHostUser}', virtualHostUser)
|
||||
currentConf = currentConf.replace('{php}', php)
|
||||
currentConf = currentConf.replace('{adminEmails}', administratorEmail)
|
||||
currentConf = currentConf.replace('{externalApp}', virtualHostUser)
|
||||
currentConf = currentConf.replace('{SSLBase}', ApacheVhost.sslBasePath)
|
||||
currentConf = currentConf.replace('{sockPath}', sockPath)
|
||||
|
||||
confFile.write(currentConf)
|
||||
confFile.close()
|
||||
|
||||
##
|
||||
|
||||
finalConfPath = ApacheVhost.DecidePHPPath(php, virtualHostName)
|
||||
|
||||
confFile = open(finalConfPath, "w+")
|
||||
currentConf = vhostConfs.phpFpmPool
|
||||
currentConf = currentConf.replace('{www}', virtualHostUser)
|
||||
currentConf = currentConf.replace('{Sock}', virtualHostName)
|
||||
currentConf = currentConf.replace('{externalApp}', externalApp)
|
||||
currentConf = currentConf.replace('{sockPath}', sockPath)
|
||||
currentConf = currentConf.replace('{group}', group)
|
||||
|
||||
confFile.write(currentConf)
|
||||
|
||||
ApacheVhost.GenerateSelfSignedSSL(virtualHostName)
|
||||
|
||||
command = f"systemctl restart {ApacheVhost.serviceName}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
return [1, 'None']
|
||||
except BaseException as msg:
|
||||
return [0, str(msg)]
|
||||
|
||||
@staticmethod
|
||||
def enableProxyInMainConf():
|
||||
try:
|
||||
data = open(ApacheVhost.lswsMainConf, 'r').readline()
|
||||
|
||||
putProxyConf = 1
|
||||
putProxyConfSSL = 1
|
||||
|
||||
for items in data:
|
||||
if items.find('apachebackend') > -1:
|
||||
putProxyConf = 0
|
||||
if items.find('proxyApacheBackendSSL') > -1:
|
||||
putProxyConfSSL = 0
|
||||
|
||||
if putProxyConf:
|
||||
confFile = open(ApacheVhost.lswsMainConf, "a")
|
||||
confFile.write(vhostConfs.proxyApacheBackend)
|
||||
confFile.close()
|
||||
|
||||
if putProxyConfSSL:
|
||||
confFile = open(ApacheVhost.lswsMainConf, "a")
|
||||
confFile.write(vhostConfs.proxyApacheBackendSSL)
|
||||
confFile.close()
|
||||
|
||||
return [1, 'None']
|
||||
except BaseException as msg:
|
||||
return [0, str(msg)]
|
||||
|
||||
@staticmethod
|
||||
def reWrite(domain_name):
|
||||
try:
|
||||
domainPath = '/home/' + domain_name + '/public_html/.htaccess'
|
||||
confFile = open(domainPath, "w+")
|
||||
confFile.write("REWRITERULE ^(.*)$ HTTP://apachebackend/$1 [P]")
|
||||
confFile.close()
|
||||
return [1, 'None']
|
||||
except BaseException as msg:
|
||||
return [0, str(msg)]
|
||||
|
||||
@staticmethod
|
||||
def setupApacheVhost(administratorEmail,externalApp, virtualHostUser, phpVersion, virtualHostName):
|
||||
result = ApacheVhost.perHostVirtualConf(administratorEmail,externalApp, virtualHostUser, phpVersion, virtualHostName)
|
||||
if result[0] == 0:
|
||||
return [0, result[1]]
|
||||
|
||||
result = ApacheVhost.enableProxyInMainConf()
|
||||
if result[0] == 0:
|
||||
return [0, result[1]]
|
||||
|
||||
return [1, 'None']
|
||||
|
||||
@staticmethod
|
||||
def perHostVirtualConfChild(administratorEmail, externalApp, virtualHostUser, phpVersion, virtualHostName, path):
|
||||
try:
|
||||
|
||||
## Non - SSL Conf
|
||||
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
sockPath = '/var/run/php-fpm/'
|
||||
group = 'nobody'
|
||||
else:
|
||||
sockPath = '/var/run/php/'
|
||||
group = 'nogroup'
|
||||
|
||||
finalConfPath = ApacheVhost.configBasePath + virtualHostName + '.conf'
|
||||
confFile = open(finalConfPath, "w+")
|
||||
|
||||
php = PHPManager.getPHPString(phpVersion)
|
||||
|
||||
currentConf = vhostConfs.apacheConfChild
|
||||
currentConf = currentConf.replace('{virtualHostName}', virtualHostName)
|
||||
currentConf = currentConf.replace('{administratorEmail}', administratorEmail)
|
||||
currentConf = currentConf.replace('{php}', php)
|
||||
currentConf = currentConf.replace('{adminEmails}', administratorEmail)
|
||||
currentConf = currentConf.replace('{externalApp}', virtualHostUser)
|
||||
currentConf = currentConf.replace('{path}', path)
|
||||
currentConf = currentConf.replace('{sockPath}', sockPath)
|
||||
|
||||
confFile.write(currentConf)
|
||||
confFile.close()
|
||||
|
||||
## SSL Conf
|
||||
|
||||
finalConfPath = ApacheVhost.configBasePath + virtualHostName + '.conf'
|
||||
confFile = open(finalConfPath, "a")
|
||||
|
||||
php = PHPManager.getPHPString(phpVersion)
|
||||
|
||||
currentConf = vhostConfs.apacheConfChildSSL
|
||||
currentConf = currentConf.replace('{virtualHostName}', virtualHostName)
|
||||
currentConf = currentConf.replace('{administratorEmail}', administratorEmail)
|
||||
currentConf = currentConf.replace('{php}', php)
|
||||
currentConf = currentConf.replace('{adminEmails}', administratorEmail)
|
||||
currentConf = currentConf.replace('{externalApp}', virtualHostUser)
|
||||
currentConf = currentConf.replace('{path}', path)
|
||||
currentConf = currentConf.replace('{sockPath}', sockPath)
|
||||
currentConf = currentConf.replace('{SSLBase}', ApacheVhost.sslBasePath)
|
||||
|
||||
confFile.write(currentConf)
|
||||
confFile.close()
|
||||
|
||||
## SSL Conf
|
||||
|
||||
finalConfPath = ApacheVhost.DecidePHPPath(php, virtualHostName)
|
||||
|
||||
confFile = open(finalConfPath, "w+")
|
||||
currentConf = vhostConfs.phpFpmPool
|
||||
currentConf = currentConf.replace('{www}', "".join(re.findall("[a-zA-Z]+", virtualHostName))[:7])
|
||||
currentConf = currentConf.replace('{Sock}', virtualHostName)
|
||||
currentConf = currentConf.replace('{externalApp}', externalApp)
|
||||
currentConf = currentConf.replace('{sockPath}', sockPath)
|
||||
currentConf = currentConf.replace('{group}', group)
|
||||
|
||||
confFile.write(currentConf)
|
||||
|
||||
ApacheVhost.GenerateSelfSignedSSL(virtualHostName)
|
||||
|
||||
command = f"systemctl restart {ApacheVhost.serviceName}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
return [1, 'None']
|
||||
except BaseException as msg:
|
||||
return [0, str(msg)]
|
||||
|
||||
@staticmethod
|
||||
def setupApacheVhostChild(administratorEmail, externalApp, virtualHostUser, phpVersion, virtualHostName, path):
|
||||
|
||||
result = ApacheVhost.perHostVirtualConfChild(administratorEmail, externalApp, virtualHostUser, phpVersion,
|
||||
virtualHostName, path)
|
||||
if result[0] == 0:
|
||||
return [0, result[1]]
|
||||
|
||||
result = ApacheVhost.enableProxyInMainConf()
|
||||
if result[0] == 0:
|
||||
return [0, result[1]]
|
||||
|
||||
return [1, 'None']
|
||||
|
||||
@staticmethod
|
||||
def DeleteApacheVhost(virtualHostName):
|
||||
try:
|
||||
finalConfPath = ApacheVhost.configBasePath + virtualHostName + '.conf'
|
||||
|
||||
if os.path.exists(finalConfPath):
|
||||
os.remove(finalConfPath)
|
||||
|
||||
ApacheVhost.deletePHPPath(virtualHostName)
|
||||
|
||||
command = f"systemctl restart {ApacheVhost.serviceName}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
except BaseException as msg:
|
||||
logging.writeToFile(str(msg))
|
||||
|
||||
@staticmethod
|
||||
def perHostVirtualConfOLS(vhFile, administratorEmail):
|
||||
# General Configurations tab
|
||||
try:
|
||||
confFile = open(vhFile, "w+")
|
||||
virtualHostName = vhFile.split('/')[6]
|
||||
|
||||
currentConf = vhostConfs.OLSLBConf
|
||||
currentConf = currentConf.replace('{adminEmails}', administratorEmail)
|
||||
currentConf = currentConf.replace('{domain}', virtualHostName)
|
||||
|
||||
confFile.write(currentConf)
|
||||
confFile.close()
|
||||
|
||||
except BaseException as msg:
|
||||
logging.writeToFile(
|
||||
str(msg) + " [IO Error with per host config file [ApacheVhosts.perHostVirtualConf]]")
|
||||
|
||||
@staticmethod
|
||||
def deletePHPPath(virtualHostName):
|
||||
|
||||
phpPath = ApacheVhost.DecidePHPPath('54', virtualHostName)
|
||||
if os.path.exists(phpPath):
|
||||
os.remove(phpPath)
|
||||
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
phpService = f'php54-php-fpm'
|
||||
else:
|
||||
phpService = f"php5.4-fpm"
|
||||
|
||||
command = f"systemctl restart {phpService}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
phpPath = ApacheVhost.DecidePHPPath('55', virtualHostName)
|
||||
if os.path.exists(phpPath):
|
||||
|
||||
os.remove(phpPath)
|
||||
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
phpService = f'php55-php-fpm'
|
||||
else:
|
||||
phpService = f"php5.5-fpm"
|
||||
|
||||
command = f"systemctl restart {phpService}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
phpPath = ApacheVhost.DecidePHPPath('56', virtualHostName)
|
||||
if os.path.exists(phpPath):
|
||||
os.remove(phpPath)
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
phpService = f'php56-php-fpm'
|
||||
else:
|
||||
phpService = f"php5.6-fpm"
|
||||
|
||||
command = f"systemctl restart {phpService}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
phpPath = ApacheVhost.DecidePHPPath('70', virtualHostName)
|
||||
if os.path.exists(phpPath):
|
||||
os.remove(phpPath)
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
phpService = f'php70-php-fpm'
|
||||
else:
|
||||
phpService = f"php7.0-fpm"
|
||||
|
||||
command = f"systemctl restart {phpService}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
phpPath = ApacheVhost.DecidePHPPath('71', virtualHostName)
|
||||
if os.path.exists(phpPath):
|
||||
os.remove(phpPath)
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
phpService = f'php71-php-fpm'
|
||||
else:
|
||||
phpService = f"php7.1-fpm"
|
||||
|
||||
command = f"systemctl restart {phpService}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
phpPath = ApacheVhost.DecidePHPPath('72', virtualHostName)
|
||||
if os.path.exists(phpPath):
|
||||
os.remove(phpPath)
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
phpService = f'php72-php-fpm'
|
||||
else:
|
||||
phpService = f"php7.2-fpm"
|
||||
|
||||
command = f"systemctl restart {phpService}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
phpPath = ApacheVhost.DecidePHPPath('73', virtualHostName)
|
||||
if os.path.exists(phpPath):
|
||||
os.remove(phpPath)
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
phpService = f'php73-php-fpm'
|
||||
else:
|
||||
phpService = f"php7.3-fpm"
|
||||
|
||||
command = f"systemctl restart {phpService}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
phpPath = ApacheVhost.DecidePHPPath('74', virtualHostName)
|
||||
if os.path.exists(phpPath):
|
||||
os.remove(phpPath)
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
phpService = f'php74-php-fpm'
|
||||
else:
|
||||
phpService = f"php7.4-fpm"
|
||||
|
||||
command = f"systemctl restart {phpService}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
phpPath = ApacheVhost.DecidePHPPath('80', virtualHostName)
|
||||
if os.path.exists(phpPath):
|
||||
os.remove(phpPath)
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
phpService = f'php80-php-fpm'
|
||||
else:
|
||||
phpService = f"php8.0-fpm"
|
||||
|
||||
command = f"systemctl restart {phpService}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
phpPath = ApacheVhost.DecidePHPPath('81', virtualHostName)
|
||||
if os.path.exists(phpPath):
|
||||
os.remove(phpPath)
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
phpService = f'php81-php-fpm'
|
||||
else:
|
||||
phpService = f"php8.1-fpm"
|
||||
|
||||
command = f"systemctl restart {phpService}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
phpPath = ApacheVhost.DecidePHPPath('82', virtualHostName)
|
||||
if os.path.exists(phpPath):
|
||||
os.remove(phpPath)
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
phpService = f'php82-php-fpm'
|
||||
else:
|
||||
phpService = f"php8.2-fpm"
|
||||
|
||||
command = f"systemctl restart {phpService}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
phpPath = ApacheVhost.DecidePHPPath('83', virtualHostName)
|
||||
if os.path.exists(phpPath):
|
||||
os.remove(phpPath)
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
phpService = f'php83-php-fpm'
|
||||
else:
|
||||
phpService = f"php8.3-fpm"
|
||||
|
||||
command = f"systemctl restart {phpService}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
phpPath = ApacheVhost.DecidePHPPath('84', virtualHostName)
|
||||
if os.path.exists(phpPath):
|
||||
os.remove(phpPath)
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
phpService = f'php84-php-fpm'
|
||||
else:
|
||||
phpService = f"php8.4-fpm"
|
||||
|
||||
command = f"systemctl restart {phpService}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
phpPath = ApacheVhost.DecidePHPPath('85', virtualHostName)
|
||||
if os.path.exists(phpPath):
|
||||
os.remove(phpPath)
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
phpService = f'php85-php-fpm'
|
||||
else:
|
||||
phpService = f"php8.5-fpm"
|
||||
|
||||
command = f"systemctl restart {phpService}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
@staticmethod
|
||||
def changePHP(phpVersion, vhFile):
|
||||
try:
|
||||
logging.writeToFile(f"PHP version passed to Apache function: {phpVersion}")
|
||||
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
sockPath = '/var/run/php-fpm/'
|
||||
group = 'nobody'
|
||||
else:
|
||||
sockPath = '/var/run/php/'
|
||||
group = 'nogroup'
|
||||
|
||||
virtualHostName = vhFile.split('/')[6]
|
||||
|
||||
finalConfPath = ApacheVhost.configBasePath + virtualHostName + '.conf'
|
||||
|
||||
if not os.path.exists(finalConfPath):
|
||||
logging.writeToFile(f'Config path: {finalConfPath}')
|
||||
return 0
|
||||
|
||||
ApacheVhost.deletePHPPath(virtualHostName)
|
||||
|
||||
try:
|
||||
website = Websites.objects.get(domain=virtualHostName)
|
||||
externalApp = website.externalApp
|
||||
except:
|
||||
child = ChildDomains.objects.get(domain=virtualHostName)
|
||||
externalApp = child.master.externalApp
|
||||
|
||||
if os.path.exists(ProcessUtilities.debugPath):
|
||||
logging.writeToFile(f"PHP version before getPHPString: {phpVersion}")
|
||||
|
||||
php = PHPManager.getPHPString(phpVersion)
|
||||
|
||||
finalConfPath = ApacheVhost.DecidePHPPath(php, virtualHostName)
|
||||
|
||||
logging.writeToFile(f'apache php final path: {finalConfPath}')
|
||||
|
||||
confFile = open(finalConfPath, "w+")
|
||||
currentConf = vhostConfs.phpFpmPool
|
||||
currentConf = currentConf.replace('{www}', externalApp)
|
||||
currentConf = currentConf.replace('{Sock}', virtualHostName)
|
||||
currentConf = currentConf.replace('{externalApp}', externalApp)
|
||||
currentConf = currentConf.replace('{sockPath}', sockPath)
|
||||
currentConf = currentConf.replace('{group}', group)
|
||||
|
||||
confFile.write(currentConf)
|
||||
|
||||
### minor bug fix of updating default php conf user in selected fpm
|
||||
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
defaultConfPath = finalConfPath.replace(virtualHostName, 'www')
|
||||
|
||||
command = f"sed -i 's/www-data/apache/g' {defaultConfPath}"
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
phpService = ApacheVhost.DecideFPMServiceName(phpVersion)
|
||||
|
||||
command = f"systemctl stop {phpService}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
command = f"systemctl restart {phpService}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
command = f"systemctl restart {ApacheVhost.serviceName}"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
return 1
|
||||
except BaseException as msg:
|
||||
logging.writeToFile(str(msg))
|
||||
return 1
|
||||
|
||||
|
||||
@staticmethod
|
||||
def DecidePHPPathforManager(apache, phpVers):
|
||||
if apache == 0 or apache == None:
|
||||
phpVers = "php" + PHPManager.getPHPString(phpVers)
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
path = "/usr/local/lsws/ls" + phpVers + "/etc/php.ini"
|
||||
else:
|
||||
initial = phpVers[3]
|
||||
final = phpVers[4]
|
||||
|
||||
completeName = str(initial) + '.' + str(final)
|
||||
path = "/usr/local/lsws/ls" + phpVers + "/etc/php/" + completeName + "/litespeed/php.ini"
|
||||
else:
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
phpVers = "php" + PHPManager.getPHPString(phpVers)
|
||||
path = f'/etc/opt/remi/{phpVers}/php.ini'
|
||||
else:
|
||||
path = f'/etc/php/{phpVers.split(" ")[1]}/fpm/php.ini'
|
||||
|
||||
if os.path.exists(ProcessUtilities.debugPath):
|
||||
logging.writeToFile(f'PHP Path {path}')
|
||||
|
||||
return path
|
||||
|
||||
@staticmethod
|
||||
def DecideFPMServiceName(phpVersion):
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
|
||||
php = PHPManager.getPHPString(phpVersion)
|
||||
return f'php{php}-php-fpm'
|
||||
else:
|
||||
return f"{phpVersion.replace(' ', '').lower()}-fpm"
|
||||
73
ApachController/BackupUtil.py
Normal file
73
ApachController/BackupUtil.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import smtplib
|
||||
import time
|
||||
import argparse
|
||||
import subprocess
|
||||
import shlex
|
||||
import os
|
||||
|
||||
|
||||
class BackupUtil:
|
||||
|
||||
@staticmethod
|
||||
def normalExecutioner(command):
|
||||
try:
|
||||
res = subprocess.call(shlex.split(command))
|
||||
if res == 0:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
except BaseException as msg:
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def SendEmail(message):
|
||||
sender = 'info@designti01.cyberhosting.org'
|
||||
receivers = ['jeanftellier@gmail.com', 'jeanftellier@gmail.com']
|
||||
|
||||
try:
|
||||
smtpObj = smtplib.SMTP('127.0.0.1')
|
||||
smtpObj.sendmail(sender, receivers, message)
|
||||
print("Successfully sent email")
|
||||
except BaseException as msg:
|
||||
print("Error: unable to send email %s" % str(msg))
|
||||
|
||||
@staticmethod
|
||||
def SyncHome():
|
||||
command = 'rsync -avz /home /mnt/HC_Volume_2760413'
|
||||
BackupUtil.normalExecutioner(command)
|
||||
message = "/home successfully synced on %s" % (time.strftime("%I-%M-%S-%a-%b-%Y"))
|
||||
BackupUtil.SendEmail(message)
|
||||
|
||||
@staticmethod
|
||||
def BackupDBS():
|
||||
command = "/usr/local/CyberCP/ApachController/backup.sh"
|
||||
BackupUtil.normalExecutioner(command)
|
||||
|
||||
message = "Database backups successfully generated on %s" % (time.strftime("%I-%M-%S-%a-%b-%Y"))
|
||||
BackupUtil.SendEmail(message)
|
||||
|
||||
@staticmethod
|
||||
def MoveAllBackups():
|
||||
|
||||
for virtualHost in os.listdir("/home"):
|
||||
completePath = "/home/%s/backup/" % (virtualHost)
|
||||
command = "mv %s %s" % (completePath + '*.tar.gz', '/home/backup/')
|
||||
subprocess.call(command, shell=True)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
parser = argparse.ArgumentParser(description='CyberPanel Backup tool.')
|
||||
parser.add_argument('function', help='Specific a function to call!')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.function == "home":
|
||||
BackupUtil.SyncHome()
|
||||
elif args.function == "db":
|
||||
BackupUtil.BackupDBS()
|
||||
elif args.function == "sync":
|
||||
BackupUtil.MoveAllBackups()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
0
ApachController/__init__.py
Normal file
0
ApachController/__init__.py
Normal file
19
ApachController/backup.sh
Normal file
19
ApachController/backup.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
USER="root"
|
||||
PASSWORD="1d1bb076c3bd9ae9ef545e3eafb1a35c68d3c5f4a6c03862"
|
||||
#OUTPUT="/Users/rabino/DBs"
|
||||
cd /mnt/HC_Volume_2760413
|
||||
|
||||
#rm "$OUTPUTDIR/*gz" > /dev/null 2>&1
|
||||
|
||||
databases=`mysql -u $USER -p$PASSWORD -e "SHOW DATABASES;" | tr -d "| " | grep -v Database`
|
||||
mkdir `date +%Y%m%d`
|
||||
|
||||
for db in $databases; do
|
||||
if [[ "$db" != "information_schema" ]] && [[ "$db" != "performance_schema" ]] && [[ "$db" != "mysql" ]] && [[ "$db" != _* ]] ; then
|
||||
echo "Dumping database: $db"
|
||||
mysqldump -u $USER -p$PASSWORD --databases $db > `date +%Y%m%d`/`date +%Y%m%d`.$db.sql
|
||||
# gzip $OUTPUT/`date +%Y%m%d`.$db.sql
|
||||
fi
|
||||
done
|
||||
459
ApachController/phpApache.xml
Normal file
459
ApachController/phpApache.xml
Normal file
@@ -0,0 +1,459 @@
|
||||
<?xml version="1.0" ?>
|
||||
<php>
|
||||
<extension>
|
||||
<extensionName>php%s-php-zstd</extensionName>
|
||||
<extensionDescription>: Zstd Extension for PHP</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-zephir-parser</extensionName>
|
||||
<extensionDescription>Zephir parser extension</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-xmlrpc</extensionName>
|
||||
<extensionDescription>A module for PHP applications which use the XML-RPC</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-xml</extensionName>
|
||||
<extensionDescription>A module for PHP applications which use XML</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-xcache</extensionName>
|
||||
<extensionDescription>Fast, stable PHP opcode cacher</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-twig</extensionName>
|
||||
<extensionDescription>The flexible, fast, and secure template engine for PHP</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-tidy</extensionName>
|
||||
<extensionDescription>Standard PHP module provides tidy library support</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-tarantool</extensionName>
|
||||
<extensionDescription>PHP driver for Tarantool/Box</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-suhosin</extensionName>
|
||||
<extensionDescription>Suhosin is an advanced protection system for PHP</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-soap</extensionName>
|
||||
<extensionDescription>A module for PHP applications that use the SOAP protocol</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-snmp</extensionName>
|
||||
<extensionDescription>A module for PHP applications that query SNMP-managed devices</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-snappy</extensionName>
|
||||
<extensionDescription>Snappy Extension for PHP</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-smbclient</extensionName>
|
||||
<extensionDescription>PHP wrapper for libsmbclient</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-recode</extensionName>
|
||||
<extensionDescription>A module for PHP applications for using the recode library</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pspell</extensionName>
|
||||
<extensionDescription>A module for PHP applications for using pspell interfaces</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-process</extensionName>
|
||||
<extensionDescription>Modules for PHP script using system process interfaces</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pimple</extensionName>
|
||||
<extensionDescription>A simple dependency injection container for PHP Extensions</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-phurple</extensionName>
|
||||
<extensionDescription>PHP bindings for libpurple</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-phpiredis</extensionName>
|
||||
<extensionDescription>Client extension for Redis</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-phalcon3</extensionName>
|
||||
<extensionDescription>Phalcon Framework</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pgsql</extensionName>
|
||||
<extensionDescription>A PostgreSQL database module for PHP</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-zmq</extensionName>
|
||||
<extensionDescription>ZeroMQ messaging</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-zip</extensionName>
|
||||
<extensionDescription>Une extension de gestion des ZIP</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-yp</extensionName>
|
||||
<extensionDescription>YP/NIS functions</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-yaz</extensionName>
|
||||
<extensionDescription>Z39.50/SRU client</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-yar</extensionName>
|
||||
<extensionDescription>Light, concurrent RPC framework</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-yaml</extensionName>
|
||||
<extensionDescription>PHP Bindings for yaml</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-yaf</extensionName>
|
||||
<extensionDescription>Extension to work with the Memcached caching daemon.</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-yac</extensionName>
|
||||
<extensionDescription>Yet Another Framework</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-yac</extensionName>
|
||||
<extensionDescription>Lockless user data cache</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-xxtea</extensionName>
|
||||
<extensionDescription>XXTEA encryption algorithm extension for PHP</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-xslcache</extensionName>
|
||||
<extensionDescription>XSL extension that caches the parsed XSL style sheet</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-xrange</extensionName>
|
||||
<extensionDescription>Numeric iterator primitives</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-xmp</extensionName>
|
||||
<extensionDescription>Bindings for the libxmp library</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-xmldiff</extensionName>
|
||||
<extensionDescription>XML diff and merge.</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-xhprof</extensionName>
|
||||
<extensionDescription>PHP extension for XHProf, a Hierarchical Profiler</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-xdiff</extensionName>
|
||||
<extensionDescription> File differences/patches.</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-xdebug</extensionName>
|
||||
<extensionDescription>PECL package for debugging PHP scripts</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-xattr</extensionName>
|
||||
<extensionDescription> Extended attributes</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-wxwidgets</extensionName>
|
||||
<extensionDescription>Cross-platform widget toolkit</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-weakref</extensionName>
|
||||
<extensionDescription>Implementation of weak references</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-vld</extensionName>
|
||||
<extensionDescription>Dump the internal representation of PHP scripts</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pecl-varnish</extensionName>
|
||||
<extensionDescription>Varnish Cache bindings</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pear.noarch</extensionName>
|
||||
<extensionDescription>PHP Extension and Application Repository framework</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-pdo</extensionName>
|
||||
<extensionDescription>A database access abstraction module for PHP applications</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-opcache</extensionName>
|
||||
<extensionDescription>The Zend OPcache</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-odbc</extensionName>
|
||||
<extensionDescription>A module for PHP applications that use ODBC databases</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-oci8</extensionName>
|
||||
<extensionDescription>A module for PHP applications that use OCI8 databases</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-mysqlnd</extensionName>
|
||||
<extensionDescription> A module for PHP applications that use MySQL Database</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-mssql</extensionName>
|
||||
<extensionDescription>MSSQL database module for PHP</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-mcrypt</extensionName>
|
||||
<extensionDescription>Standard PHP module provides mcrypt library support</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-mbstring</extensionName>
|
||||
<extensionDescription>A module for PHP applications which need multi-byte String handle</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-maxminddb</extensionName>
|
||||
<extensionDescription>MaxMind DB Reader extension</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-magickwand</extensionName>
|
||||
<extensionDescription>PHP API for ImageMagick</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-lz4</extensionName>
|
||||
<extensionDescription>LZ4 Extension for PHP</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-libvirt-doc</extensionName>
|
||||
<extensionDescription>Document of php-libvirt</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-libvirt</extensionName>
|
||||
<extensionDescription>PHP language binding for Libvirt</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-ldap</extensionName>
|
||||
<extensionDescription>A module for PHP applications that use LDAP</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-ioncube-loader</extensionName>
|
||||
<extensionDescription>Loader for ionCube Encoded Files with ionCube</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-intl</extensionName>
|
||||
<extensionDescription>Internationalization extension for PHP applications</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-interbase</extensionName>
|
||||
<extensionDescription> A module for PHP applications that use Interbase/Firebird databases</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-imap</extensionName>
|
||||
<extensionDescription>A module for PHP applications that use IMAPs</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-horde-horde-lz4</extensionName>
|
||||
<extensionDescription>Horde LZ4 Compression Extension</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-gmp</extensionName>
|
||||
<extensionDescription>A module for PHP applications for using the GNU MP library</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-geos</extensionName>
|
||||
<extensionDescription>PHP module for GEOS</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-gd</extensionName>
|
||||
<extensionDescription> A module for PHP applications for using the gd graphics library</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-enchant</extensionName>
|
||||
<extensionDescription> Enchant spelling extension for PHP applications</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-embedded</extensionName>
|
||||
<extensionDescription>PHP library for embedding in applications</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-devel</extensionName>
|
||||
<extensionDescription>Files needed for building PHP extensions</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-dbg</extensionName>
|
||||
<extensionDescription>The interactive PHP debugger</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-dba</extensionName>
|
||||
<extensionDescription>A database abstraction layer module for PHP applications</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-common</extensionName>
|
||||
<extensionDescription>Common files for PHP</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-cli</extensionName>
|
||||
<extensionDescription>Command-line interface for PHP</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-channel-horde.noarch</extensionName>
|
||||
<extensionDescription>Adds pear.horde.org channel to PEAR</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-brotli</extensionName>
|
||||
<extensionDescription>Brotli Extension for PHP</extensionDescription>
|
||||
<status>0</status>
|
||||
</extension>
|
||||
|
||||
<extension>
|
||||
<extensionName>php%s-php-bcmath</extensionName>
|
||||
<extensionDescription>A module for PHP applications for using the bcmath library</extensionDescription>
|
||||
<status>1</status>
|
||||
</extension>
|
||||
|
||||
</php>
|
||||
BIN
CLManager/.DS_Store
vendored
Normal file
BIN
CLManager/.DS_Store
vendored
Normal file
Binary file not shown.
220
CLManager/CLManagerMain.py
Normal file
220
CLManager/CLManagerMain.py
Normal file
@@ -0,0 +1,220 @@
|
||||
import threading as multi
|
||||
from plogical.acl import ACLManager
|
||||
import plogical.CyberCPLogFileWriter as logging
|
||||
from plogical.processUtilities import ProcessUtilities
|
||||
from django.shortcuts import render
|
||||
import os
|
||||
from serverStatus.serverStatusUtil import ServerStatusUtil
|
||||
import json
|
||||
from django.shortcuts import HttpResponse
|
||||
from math import ceil
|
||||
from websiteFunctions.models import Websites
|
||||
from CLManager.models import CLPackages
|
||||
from plogical.httpProc import httpProc
|
||||
|
||||
class CLManagerMain(multi.Thread):
|
||||
|
||||
def __init__(self, request=None, templateName=None, function=None, data=None):
|
||||
multi.Thread.__init__(self)
|
||||
self.request = request
|
||||
self.templateName = templateName
|
||||
self.function = function
|
||||
self.data = data
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
if self.function == 'submitCageFSInstall':
|
||||
self.submitCageFSInstall()
|
||||
elif self.function == 'enableOrDisable':
|
||||
self.enableOrDisable()
|
||||
except BaseException as msg:
|
||||
logging.CyberCPLogFileWriter.writeToFile(str(msg) + ' [ContainerManager.run]')
|
||||
|
||||
def renderC(self):
|
||||
|
||||
data = {}
|
||||
data['CL'] = 0
|
||||
data['activatedPath'] = 0
|
||||
CLPath = '/etc/sysconfig/cloudlinux'
|
||||
activatedPath = '/home/cyberpanel/cloudlinux'
|
||||
|
||||
# Debug logging
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] Starting CloudLinux detection...")
|
||||
|
||||
# Check multiple ways to detect CloudLinux
|
||||
# Method 1: Check /etc/sysconfig/cloudlinux
|
||||
if os.path.exists(CLPath):
|
||||
data['CL'] = 1
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] CloudLinux detected via {CLPath}")
|
||||
# Method 2: Check /etc/redhat-release
|
||||
elif os.path.exists('/etc/redhat-release'):
|
||||
try:
|
||||
with open('/etc/redhat-release', 'r') as f:
|
||||
content = f.read()
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] /etc/redhat-release content: {content.strip()}")
|
||||
if 'CloudLinux' in content:
|
||||
data['CL'] = 1
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] CloudLinux detected in /etc/redhat-release")
|
||||
else:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] CloudLinux NOT found in /etc/redhat-release")
|
||||
except Exception as e:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] Error reading /etc/redhat-release: {str(e)}")
|
||||
# Method 3: Check /etc/os-release
|
||||
elif os.path.exists('/etc/os-release'):
|
||||
try:
|
||||
with open('/etc/os-release', 'r') as f:
|
||||
content = f.read()
|
||||
if 'CloudLinux' in content:
|
||||
data['CL'] = 1
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] CloudLinux detected in /etc/os-release")
|
||||
except:
|
||||
pass
|
||||
# Method 4: Check if cagefsctl command exists
|
||||
elif os.path.exists('/usr/sbin/cagefsctl'):
|
||||
data['CL'] = 1
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] CloudLinux detected via cagefsctl presence")
|
||||
else:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f"[CLManager] CloudLinux not detected by any method")
|
||||
|
||||
if os.path.exists(activatedPath):
|
||||
data['activatedPath'] = 1
|
||||
|
||||
if data['CL'] == 0:
|
||||
proc = httpProc(self.request, 'CLManager/notAvailable.html', data, 'admin')
|
||||
return proc.render()
|
||||
elif data['activatedPath'] == 0:
|
||||
proc = httpProc(self.request, 'CLManager/notAvailable.html', data, 'admin')
|
||||
return proc.render()
|
||||
else:
|
||||
proc = httpProc(self.request, 'CLManager/cloudLinux.html', data, 'admin')
|
||||
return proc.render()
|
||||
|
||||
def submitCageFSInstall(self):
|
||||
try:
|
||||
userID = self.request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
'Not authorized to install container packages. [404].',
|
||||
1)
|
||||
return 0
|
||||
|
||||
execPath = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/CLManager/CageFS.py"
|
||||
execPath = execPath + " --function submitCageFSInstall"
|
||||
ProcessUtilities.outputExecutioner(execPath)
|
||||
|
||||
except BaseException as msg:
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath, str(msg) + ' [404].', 1)
|
||||
|
||||
def findWebsitesJson(self, currentACL, userID, pageNumber):
|
||||
finalPageNumber = ((pageNumber * 10)) - 10
|
||||
endPageNumber = finalPageNumber + 10
|
||||
websites = ACLManager.findWebsiteObjects(currentACL, userID)[finalPageNumber:endPageNumber]
|
||||
|
||||
json_data = "["
|
||||
checker = 0
|
||||
|
||||
command = '/usr/sbin/cagefsctl --list-enabled'
|
||||
Enabled = ProcessUtilities.outputExecutioner(command)
|
||||
|
||||
for items in websites:
|
||||
if Enabled.find(items.externalApp) > -1:
|
||||
status = 1
|
||||
else:
|
||||
status = 0
|
||||
dic = {'domain': items.domain, 'externalApp': items.externalApp, 'status': status}
|
||||
|
||||
if checker == 0:
|
||||
json_data = json_data + json.dumps(dic)
|
||||
checker = 1
|
||||
else:
|
||||
json_data = json_data + ',' + json.dumps(dic)
|
||||
|
||||
json_data = json_data + ']'
|
||||
|
||||
return json_data
|
||||
|
||||
def websitePagination(self, currentACL, userID):
|
||||
websites = ACLManager.findAllSites(currentACL, userID)
|
||||
|
||||
pages = float(len(websites)) / float(10)
|
||||
pagination = []
|
||||
|
||||
if pages <= 1.0:
|
||||
pages = 1
|
||||
pagination.append('<li><a href="\#"></a></li>')
|
||||
else:
|
||||
pages = ceil(pages)
|
||||
finalPages = int(pages) + 1
|
||||
|
||||
for i in range(1, finalPages):
|
||||
pagination.append('<li><a href="\#">' + str(i) + '</a></li>')
|
||||
|
||||
return pagination
|
||||
|
||||
def getFurtherAccounts(self, userID=None, data=None):
|
||||
try:
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
pageNumber = int(data['page'])
|
||||
json_data = self.findWebsitesJson(currentACL, userID, pageNumber)
|
||||
pagination = self.websitePagination(currentACL, userID)
|
||||
|
||||
cageFSPath = '/home/cyberpanel/cagefs'
|
||||
|
||||
if os.path.exists(cageFSPath):
|
||||
default = 'On'
|
||||
else:
|
||||
default = 'Off'
|
||||
|
||||
final_dic = {'status': 1, 'listWebSiteStatus': 1, 'error_message': "None", "data": json_data,
|
||||
'pagination': pagination, 'default': default}
|
||||
final_json = json.dumps(final_dic)
|
||||
return HttpResponse(final_json)
|
||||
except BaseException as msg:
|
||||
dic = {'status': 1, 'listWebSiteStatus': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(dic)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def enableOrDisable(self):
|
||||
try:
|
||||
websites = Websites.objects.all()
|
||||
if self.data['mode'] == 1:
|
||||
for items in websites:
|
||||
command = '/usr/sbin/cagefsctl --enable %s' % (items.externalApp)
|
||||
ProcessUtilities.executioner(command)
|
||||
else:
|
||||
for items in websites:
|
||||
command = '/usr/sbin/cagefsctl --disable %s' % (items.externalApp)
|
||||
ProcessUtilities.executioner(command)
|
||||
except BaseException as msg:
|
||||
logging.CyberCPLogFileWriter.writeToFile(str(msg))
|
||||
|
||||
def fetchPackages(self, currentACL):
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
json_data = "["
|
||||
checker = 0
|
||||
|
||||
for items in CLPackages.objects.all():
|
||||
dic = {'name': items.name, 'SPEED': items.speed, 'VMEM': items.vmem, 'PMEM': items.pmem, 'IO': items.io, 'IOPS': items.iops, 'EP': items.ep,
|
||||
'NPROC': items.nproc, 'inodessoft': items.inodessoft, 'inodeshard': items.inodeshard}
|
||||
|
||||
if checker == 0:
|
||||
json_data = json_data + json.dumps(dic)
|
||||
checker = 1
|
||||
else:
|
||||
json_data = json_data + ',' + json.dumps(dic)
|
||||
|
||||
json_data = json_data + ']'
|
||||
|
||||
final_dic = {'status': 1, 'error_message': "None", "data": json_data}
|
||||
final_json = json.dumps(final_dic)
|
||||
return HttpResponse(final_json)
|
||||
|
||||
82
CLManager/CLPackages.py
Normal file
82
CLManager/CLPackages.py
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/usr/local/CyberCP/bin/python
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
import django
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
|
||||
django.setup()
|
||||
import argparse
|
||||
from websiteFunctions.models import Websites
|
||||
from CLManager.models import CLPackages
|
||||
import pwd
|
||||
|
||||
class CLinuxPackages:
|
||||
|
||||
@staticmethod
|
||||
def listAll():
|
||||
for items in Websites.objects.all():
|
||||
itemPackage = items.package
|
||||
try:
|
||||
clPackage = CLPackages.objects.get(owner=itemPackage)
|
||||
statement = '%s %s' % (pwd.getpwnam(items.externalApp).pw_uid, clPackage.name)
|
||||
print(statement)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@staticmethod
|
||||
def listPackages():
|
||||
for items in CLPackages.objects.all():
|
||||
print(items.name)
|
||||
|
||||
@staticmethod
|
||||
def userIDPackage(user):
|
||||
website = Websites.objects.get(externalApp=user)
|
||||
itemPackage = website.package
|
||||
try:
|
||||
clPackage = CLPackages.objects.get(owner=itemPackage)
|
||||
print(clPackage)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@staticmethod
|
||||
def packageForUser(package):
|
||||
for items in Websites.objects.all():
|
||||
itemPackage = items.package
|
||||
try:
|
||||
clPackage = CLPackages.objects.get(owner=itemPackage)
|
||||
if clPackage.name == package:
|
||||
print(pwd.getpwnam(items.externalApp).pw_uid)
|
||||
except:
|
||||
pass
|
||||
|
||||
def main():
|
||||
|
||||
parser = argparse.ArgumentParser(description='CyberPanel Container Manager')
|
||||
parser.add_argument('--userid', help='User ID')
|
||||
parser.add_argument('--package', help='Package')
|
||||
parser.add_argument('--function', help='Function')
|
||||
parser.add_argument('--list-all', help='List all users/packages.', action='store_true')
|
||||
parser.add_argument('--list-packages', help='List all packages.', action='store_true')
|
||||
|
||||
|
||||
args = vars(parser.parse_args())
|
||||
|
||||
if args['userid']:
|
||||
CLinuxPackages.userIDPackage(args['userid'])
|
||||
elif args['package']:
|
||||
CLinuxPackages.packageForUser(args['package'])
|
||||
elif args['list_all']:
|
||||
CLinuxPackages.listAll()
|
||||
elif args['list_packages']:
|
||||
CLinuxPackages.listPackages()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
302
CLManager/CageFS.py
Normal file
302
CLManager/CageFS.py
Normal file
@@ -0,0 +1,302 @@
|
||||
#!/usr/local/CyberCP/bin/python
|
||||
import sys
|
||||
import os
|
||||
import django
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
|
||||
|
||||
django.setup()
|
||||
import plogical.CyberCPLogFileWriter as logging
|
||||
import argparse
|
||||
from plogical.mailUtilities import mailUtilities
|
||||
from plogical.processUtilities import ProcessUtilities
|
||||
from plogical.firewallUtilities import FirewallUtilities
|
||||
from firewall.models import FirewallRules
|
||||
from serverStatus.serverStatusUtil import ServerStatusUtil
|
||||
|
||||
|
||||
class CageFS:
|
||||
packages = ['talksho']
|
||||
users = ['5001']
|
||||
|
||||
@staticmethod
|
||||
def EnableCloudLinux():
|
||||
if ProcessUtilities.decideServer() == ProcessUtilities.OLS:
|
||||
confPath = '/usr/local/lsws/conf/httpd_config.conf'
|
||||
data = open(confPath, 'r').readlines()
|
||||
|
||||
writeToFile = open(confPath, 'w')
|
||||
|
||||
for items in data:
|
||||
if items.find('priority') > -1:
|
||||
writeToFile.writelines(items)
|
||||
writeToFile.writelines('enableLVE 2\n')
|
||||
else:
|
||||
writeToFile.writelines(items)
|
||||
|
||||
writeToFile.close()
|
||||
else:
|
||||
confPath = '/usr/local/lsws/conf/httpd_config.xml'
|
||||
data = open(confPath, 'r').readlines()
|
||||
|
||||
writeToFile = open(confPath, 'w')
|
||||
|
||||
for items in data:
|
||||
if items.find('<enableChroot>') > -1:
|
||||
writeToFile.writelines(items)
|
||||
writeToFile.writelines(' <enableLVE>2</enableLVE>\n')
|
||||
else:
|
||||
writeToFile.writelines(items)
|
||||
|
||||
writeToFile.close()
|
||||
|
||||
ProcessUtilities.restartLitespeed()
|
||||
|
||||
@staticmethod
|
||||
def submitCageFSInstall():
|
||||
try:
|
||||
|
||||
mailUtilities.checkHome()
|
||||
|
||||
statusFile = open(ServerStatusUtil.lswsInstallStatusPath, 'w')
|
||||
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
"Checking if LVE Kernel is loaded ..\n", 1)
|
||||
|
||||
if ProcessUtilities.outputExecutioner('uname -a').find('lve') > -1 or ProcessUtilities.outputExecutioner('lsmod').find('lve') > -1:
|
||||
pass
|
||||
else:
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
"CloudLinux is installed but kernel is not loaded, please reboot your server to load appropriate kernel. [404]\n", 1)
|
||||
return 0
|
||||
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
"CloudLinux Kernel detected..\n", 1)
|
||||
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
"Enabling CloudLinux in web server ..\n", 1)
|
||||
|
||||
CageFS.EnableCloudLinux()
|
||||
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
"CloudLinux enabled in server ..\n", 1)
|
||||
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
"Adding LVEManager port ..\n", 1)
|
||||
try:
|
||||
FirewallUtilities.addRule('tcp', '9000', '0.0.0.0/0')
|
||||
|
||||
newFWRule = FirewallRules(name='lvemanager', proto='tcp', port='9000', ipAddress='0.0.0.0/0')
|
||||
newFWRule.save()
|
||||
except:
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
"LVEManager port added ..\n", 1)
|
||||
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
"Reinstalling important components ..\n", 1)
|
||||
|
||||
command = 'yum install -y alt-python37-devel'
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
command = 'yum reinstall -y cloudlinux-venv'
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
command = 'yum reinstall -y lvemanager lve-utils cagefs'
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
command = 'yum reinstall -y cloudlinux-venv'
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
command = 'systemctl restart lvemanager'
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
"Important components reinstalled..\n", 1)
|
||||
|
||||
activatedPath = '/home/cyberpanel/cloudlinux'
|
||||
|
||||
writeToFile = open(activatedPath, 'a')
|
||||
writeToFile.write('CLInstalled')
|
||||
writeToFile.close()
|
||||
|
||||
|
||||
|
||||
#### mount session save paths
|
||||
|
||||
if os.path.exists('/etc/cagefs/cagefs.mp'):
|
||||
|
||||
from managePHP.phpManager import PHPManager
|
||||
php_versions = PHPManager.findPHPVersions()
|
||||
|
||||
for php in php_versions:
|
||||
PHPVers = PHPManager.getPHPString(php)
|
||||
line = f'@/var/lib/lsphp/session/lsphp{PHPVers},700\n'
|
||||
|
||||
WriteToFile = open('/etc/cagefs/cagefs.mp', 'a')
|
||||
WriteToFile.write(line)
|
||||
WriteToFile.close()
|
||||
|
||||
command = 'cagefsctl --remount-all'
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
"Packages successfully installed.[200]\n", 1)
|
||||
|
||||
except BaseException as msg:
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath, str(msg) + ' [404].', 1)
|
||||
|
||||
@staticmethod
|
||||
def submitinstallImunify(key):
|
||||
try:
|
||||
|
||||
imunifyKeyPath = '/home/cyberpanel/imunifyKeyPath'
|
||||
|
||||
##
|
||||
|
||||
writeToFile = open(imunifyKeyPath, 'w')
|
||||
writeToFile.write(key)
|
||||
writeToFile.close()
|
||||
|
||||
##
|
||||
|
||||
mailUtilities.checkHome()
|
||||
|
||||
statusFile = open(ServerStatusUtil.lswsInstallStatusPath, 'w')
|
||||
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
"Starting Imunify Installation..\n", 1)
|
||||
|
||||
##
|
||||
|
||||
command = 'mkdir -p /etc/sysconfig/imunify360/generic'
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
command = 'touch /etc/sysconfig/imunify360/generic/modsec.conf'
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
integrationFile = '/etc/sysconfig/imunify360/integration.conf'
|
||||
|
||||
content = """[paths]
|
||||
ui_path =/usr/local/CyberCP/public/imunify
|
||||
[web_server]
|
||||
server_type = litespeed
|
||||
graceful_restart_script = /usr/local/lsws/bin/lswsctrl restart
|
||||
modsec_audit_log = /usr/local/lsws/logs/auditmodsec.log
|
||||
modsec_audit_logdir = /usr/local/lsws/logs/
|
||||
|
||||
[malware]
|
||||
basedir = /home
|
||||
pattern_to_watch = ^/home/.+?/(public_html|public_ftp|private_html)(/.*)?$
|
||||
"""
|
||||
|
||||
writeToFile = open(integrationFile, 'w')
|
||||
writeToFile.write(content)
|
||||
writeToFile.close()
|
||||
|
||||
##
|
||||
|
||||
### address issue to create imunify dir - https://app.clickup.com/t/86engx249
|
||||
|
||||
command = 'mkdir /usr/local/CyberCP/public/imunify'
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
command = 'pkill -f "bash i360deploy.sh"'
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
if not os.path.exists('i360deploy.sh'):
|
||||
command = 'wget https://repo.imunify360.cloudlinux.com/defence360/i360deploy.sh'
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
command = 'bash i360deploy.sh --uninstall --yes'
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
command = 'bash i360deploy.sh --key %s --yes' % (key)
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
"Imunify reinstalled..\n", 1)
|
||||
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
"Packages successfully installed.[200]\n", 1)
|
||||
|
||||
except BaseException as msg:
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath, str(msg) + ' [404].', 1)
|
||||
|
||||
@staticmethod
|
||||
def submitinstallImunifyAV():
|
||||
try:
|
||||
|
||||
|
||||
mailUtilities.checkHome()
|
||||
|
||||
statusFile = open(ServerStatusUtil.lswsInstallStatusPath, 'w')
|
||||
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
"Starting ImunifyAV Installation..\n", 1)
|
||||
|
||||
##
|
||||
|
||||
command = 'mkdir -p /etc/sysconfig/imunify360'
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
|
||||
integrationFile = '/etc/sysconfig/imunify360/integration.conf'
|
||||
|
||||
content = """[paths]
|
||||
ui_path = /usr/local/CyberCP/public/imunifyav
|
||||
ui_path_owner = lscpd:lscpd
|
||||
"""
|
||||
|
||||
writeToFile = open(integrationFile, 'w')
|
||||
writeToFile.write(content)
|
||||
writeToFile.close()
|
||||
|
||||
##
|
||||
|
||||
### address issue to create imunify dir - https://app.clickup.com/t/86engx249
|
||||
|
||||
command = 'mkdir /usr/local/CyberCP/public/imunifyav'
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
command = 'pkill -f "bash imav-deploy.sh"'
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
if not os.path.exists('imav-deploy.sh'):
|
||||
command = 'wget https://repo.imunify360.cloudlinux.com/defence360/imav-deploy.sh'
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
command = 'bash imav-deploy.sh --uninstall --yes'
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
command = 'bash imav-deploy.sh --yes'
|
||||
ServerStatusUtil.executioner(command, statusFile)
|
||||
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
"ImunifyAV reinstalled..\n", 1)
|
||||
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath,
|
||||
"Packages successfully installed.[200]\n", 1)
|
||||
|
||||
except BaseException as msg:
|
||||
logging.CyberCPLogFileWriter.statusWriter(ServerStatusUtil.lswsInstallStatusPath, str(msg) + ' [404].', 1)
|
||||
|
||||
def main():
|
||||
|
||||
parser = argparse.ArgumentParser(description='CyberPanel CageFS Manager')
|
||||
parser.add_argument('--function', help='Function')
|
||||
parser.add_argument('--key', help='Imunify Key')
|
||||
|
||||
|
||||
args = vars(parser.parse_args())
|
||||
|
||||
if args["function"] == "submitCageFSInstall":
|
||||
CageFS.submitCageFSInstall()
|
||||
elif args["function"] == "submitinstallImunify":
|
||||
CageFS.submitinstallImunify(args["key"])
|
||||
elif args["function"] == "submitinstallImunifyAV":
|
||||
CageFS.submitinstallImunifyAV()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
0
CLManager/__init__.py
Normal file
0
CLManager/__init__.py
Normal file
6
CLManager/admin.py
Normal file
6
CLManager/admin.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
8
CLManager/apps.py
Normal file
8
CLManager/apps.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ClmanagerConfig(AppConfig):
|
||||
name = 'CLManager'
|
||||
0
CLManager/migrations/__init__.py
Normal file
0
CLManager/migrations/__init__.py
Normal file
20
CLManager/models.py
Normal file
20
CLManager/models.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.db import models
|
||||
from packages.models import Package
|
||||
|
||||
# Create your models here.
|
||||
|
||||
class CLPackages(models.Model):
|
||||
owner = models.ForeignKey(Package, on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=50,unique=True)
|
||||
speed = models.CharField(max_length=50)
|
||||
vmem = models.CharField(max_length=50)
|
||||
pmem = models.CharField(max_length=50)
|
||||
io = models.CharField(max_length=50)
|
||||
iops = models.CharField(max_length=50)
|
||||
ep = models.CharField(max_length=50)
|
||||
nproc = models.CharField(max_length=50)
|
||||
inodessoft = models.CharField(max_length=50)
|
||||
inodeshard = models.CharField(max_length=50)
|
||||
933
CLManager/static/CLManager/CLManager.js
Normal file
933
CLManager/static/CLManager/CLManager.js
Normal file
@@ -0,0 +1,933 @@
|
||||
app.controller('installCageFS', function ($scope, $http, $timeout, $window) {
|
||||
|
||||
$scope.installDockerStatus = true;
|
||||
$scope.installBoxGen = true;
|
||||
$scope.dockerInstallBTN = false;
|
||||
|
||||
$scope.submitCageFSInstall = function () {
|
||||
|
||||
$scope.installDockerStatus = false;
|
||||
$scope.installBoxGen = true;
|
||||
$scope.dockerInstallBTN = true;
|
||||
|
||||
url = "/CloudLinux/submitCageFSInstall";
|
||||
|
||||
var data = {};
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
|
||||
|
||||
|
||||
function ListInitialDatas(response) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
if (response.data.status === 1) {
|
||||
$scope.installBoxGen = false;
|
||||
getRequestStatus();
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function cantLoadInitialDatas(response) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: 'Could not connect to server, please refresh this page',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function getRequestStatus() {
|
||||
$scope.installDockerStatus = false;
|
||||
|
||||
url = "/serverstatus/switchTOLSWSStatus";
|
||||
|
||||
var data = {};
|
||||
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
|
||||
|
||||
|
||||
function ListInitialDatas(response) {
|
||||
if (response.data.abort === 0) {
|
||||
$scope.requestData = response.data.requestStatus;
|
||||
$timeout(getRequestStatus, 1000);
|
||||
} else {
|
||||
// Notifications
|
||||
$scope.installDockerStatus = true;
|
||||
$timeout.cancel();
|
||||
$scope.requestData = response.data.requestStatus;
|
||||
if (response.data.installed === 1) {
|
||||
$timeout(function () {
|
||||
$window.location.reload();
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function cantLoadInitialDatas(response) {
|
||||
$scope.installDockerStatus = true;
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: 'Could not connect to server, please refresh this page',
|
||||
type: 'error'
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
app.controller('listWebsitesCage', function ($scope, $http) {
|
||||
|
||||
var globalPageNumber;
|
||||
$scope.getFurtherWebsitesFromDB = function (pageNumber) {
|
||||
$scope.cyberPanelLoading = false;
|
||||
globalPageNumber = pageNumber;
|
||||
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
var data = {page: pageNumber};
|
||||
|
||||
|
||||
dataurl = "/CloudLinux/submitWebsiteListing";
|
||||
|
||||
$http.post(dataurl, data, config).then(ListInitialData, cantLoadInitialData);
|
||||
|
||||
|
||||
function ListInitialData(response) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
if (response.data.listWebSiteStatus === 1) {
|
||||
var finalData = JSON.parse(response.data.data);
|
||||
$scope.WebSitesList = finalData;
|
||||
$scope.pagination = response.data.pagination;
|
||||
$scope.default = response.data.default;
|
||||
$("#listFail").hide();
|
||||
} else {
|
||||
$("#listFail").fadeIn();
|
||||
$scope.errorMessage = response.data.error_message;
|
||||
console.log(response.data);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function cantLoadInitialData(response) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
console.log("not good");
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
$scope.getFurtherWebsitesFromDB(1);
|
||||
|
||||
$scope.cyberPanelLoading = true;
|
||||
|
||||
$scope.searchWebsites = function () {
|
||||
|
||||
$scope.cyberPanelLoading = false;
|
||||
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
var data = {
|
||||
patternAdded: $scope.patternAdded
|
||||
};
|
||||
|
||||
dataurl = "/websites/searchWebsites";
|
||||
|
||||
$http.post(dataurl, data, config).then(ListInitialData, cantLoadInitialData);
|
||||
|
||||
|
||||
function ListInitialData(response) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
if (response.data.listWebSiteStatus === 1) {
|
||||
|
||||
var finalData = JSON.parse(response.data.data);
|
||||
$scope.WebSitesList = finalData;
|
||||
$("#listFail").hide();
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function cantLoadInitialData(response) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: 'Connect disrupted, refresh the page.',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
$scope.enableOrDisable = function (domain, all, mode, toggle = 0) {
|
||||
$scope.cyberPanelLoading = false;
|
||||
|
||||
url = "/CloudLinux/enableOrDisable";
|
||||
|
||||
var data = {
|
||||
domain: domain,
|
||||
all: all,
|
||||
mode: mode,
|
||||
toggle: toggle
|
||||
};
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
|
||||
|
||||
|
||||
function ListInitialDatas(response) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
if (response.data.status === 1) {
|
||||
new PNotify({
|
||||
title: 'Success',
|
||||
text: response.data.success,
|
||||
type: 'success'
|
||||
});
|
||||
|
||||
if (all === 0) {
|
||||
$scope.getFurtherWebsitesFromDB(globalPageNumber);
|
||||
}
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function cantLoadInitialDatas(response) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: 'Could not connect to server, please refresh this page',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$scope.refreshStatus = function () {
|
||||
$scope.getFurtherWebsitesFromDB(globalPageNumber);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
app.controller('createCLPackage', function ($scope, $http) {
|
||||
|
||||
$scope.cyberPanelLoading = true;
|
||||
$scope.modifyPackageForm = true;
|
||||
$scope.toggleView = function () {
|
||||
$scope.modifyPackageForm = false;
|
||||
};
|
||||
|
||||
$scope.createPackage = function () {
|
||||
$scope.cyberPanelLoading = false;
|
||||
|
||||
url = "/CloudLinux/submitCreatePackage";
|
||||
|
||||
var data = {
|
||||
selectedPackage: $scope.selectedPackage,
|
||||
name: $scope.name,
|
||||
SPEED: $scope.SPEED,
|
||||
VMEM: $scope.VMEM,
|
||||
PMEM: $scope.PMEM,
|
||||
IO: $scope.IO,
|
||||
IOPS: $scope.IOPS,
|
||||
EP: $scope.EP,
|
||||
NPROC: $scope.NPROC,
|
||||
INODESsoft: $scope.INODESsoft,
|
||||
INODEShard: $scope.INODEShard,
|
||||
};
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
|
||||
|
||||
|
||||
function ListInitialDatas(response) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
if (response.data.status === 1) {
|
||||
new PNotify({
|
||||
title: 'Success',
|
||||
text: 'Successfully created.',
|
||||
type: 'success'
|
||||
});
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function cantLoadInitialDatas(response) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: 'Could not connect to server, please refresh this page',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
app.controller('listCloudLinuxPackages', function ($scope, $http) {
|
||||
|
||||
$scope.cyberPanelLoading = true;
|
||||
|
||||
$scope.fetchPackageas = function () {
|
||||
$scope.cyberPanelLoading = false;
|
||||
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
var data = {};
|
||||
|
||||
|
||||
dataurl = "/CloudLinux/fetchPackages";
|
||||
|
||||
$http.post(dataurl, data, config).then(ListInitialData, cantLoadInitialData);
|
||||
|
||||
|
||||
function ListInitialData(response) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
if (response.data.status === 1) {
|
||||
$scope.packages = JSON.parse(response.data.data);
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function cantLoadInitialData(response) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: 'Could not connect to server, please refresh this page',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
$scope.fetchPackageas();
|
||||
|
||||
$scope.deleteCLPackage = function (name) {
|
||||
$scope.cyberPanelLoading = false;
|
||||
|
||||
url = "/CloudLinux/deleteCLPackage";
|
||||
|
||||
var data = {
|
||||
name: name
|
||||
};
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
|
||||
|
||||
|
||||
function ListInitialDatas(response) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
if (response.data.status === 1) {
|
||||
new PNotify({
|
||||
title: 'Success',
|
||||
text: 'Successfully deleted.',
|
||||
type: 'success'
|
||||
});
|
||||
$scope.fetchPackageas();
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function cantLoadInitialDatas(response) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: 'Could not connect to server, please refresh this page',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
$scope.populatePackage = function (name, speed, vmem, pmem, io, iops, ep, nproc, inodessoft, inodeshard) {
|
||||
$scope.name = name;
|
||||
$scope.SPEED = speed;
|
||||
$scope.VMEM = vmem;
|
||||
$scope.PMEM = pmem;
|
||||
$scope.IO = io;
|
||||
$scope.IOPS = iops;
|
||||
$scope.EP = ep;
|
||||
$scope.NPROC = nproc;
|
||||
$scope.inodessoft = inodessoft;
|
||||
$scope.inodeshard = inodeshard;
|
||||
|
||||
};
|
||||
|
||||
$scope.saveSettings = function () {
|
||||
$scope.cyberPanelLoading = false;
|
||||
|
||||
url = "/CloudLinux/saveSettings";
|
||||
|
||||
var data = {
|
||||
name: $scope.name,
|
||||
SPEED: $scope.SPEED,
|
||||
VMEM: $scope.VMEM,
|
||||
PMEM: $scope.PMEM,
|
||||
IO: $scope.IO,
|
||||
IOPS: $scope.IOPS,
|
||||
EP: $scope.EP,
|
||||
NPROC: $scope.NPROC,
|
||||
INODESsoft: $scope.inodessoft,
|
||||
INODEShard: $scope.inodeshard,
|
||||
};
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
|
||||
|
||||
|
||||
function ListInitialDatas(response) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
if (response.data.status === 1) {
|
||||
new PNotify({
|
||||
title: 'Success',
|
||||
text: 'Changes successfully applied.',
|
||||
type: 'success'
|
||||
});
|
||||
$scope.fetchPackageas();
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function cantLoadInitialDatas(response) {
|
||||
$scope.cyberPanelLoading = true;
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: 'Could not connect to server, please refresh this page',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
app.controller('websiteContainerLimitCL', function ($scope, $http, $timeout, $window) {
|
||||
|
||||
|
||||
// Get CPU Usage of User
|
||||
|
||||
var cpu = [];
|
||||
var dataset;
|
||||
var totalPoints = 100;
|
||||
var updateInterval = 1000;
|
||||
var now = new Date().getTime();
|
||||
|
||||
var options = {
|
||||
series: {
|
||||
lines: {
|
||||
lineWidth: 1.2
|
||||
},
|
||||
bars: {
|
||||
align: "center",
|
||||
fillColor: {colors: [{opacity: 1}, {opacity: 1}]},
|
||||
barWidth: 500,
|
||||
lineWidth: 1
|
||||
}
|
||||
},
|
||||
xaxis: {
|
||||
mode: "time",
|
||||
tickSize: [5, "second"],
|
||||
tickFormatter: function (v, axis) {
|
||||
var date = new Date(v);
|
||||
|
||||
if (date.getSeconds() % 20 == 0) {
|
||||
var hours = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
|
||||
var minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
|
||||
var seconds = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
|
||||
|
||||
return hours + ":" + minutes + ":" + seconds;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
axisLabel: "Time",
|
||||
axisLabelUseCanvas: true,
|
||||
axisLabelFontSizePixels: 12,
|
||||
axisLabelFontFamily: 'Verdana, Arial',
|
||||
axisLabelPadding: 10
|
||||
},
|
||||
yaxes: [
|
||||
{
|
||||
min: 0,
|
||||
max: 100,
|
||||
tickSize: 5,
|
||||
tickFormatter: function (v, axis) {
|
||||
if (v % 10 == 0) {
|
||||
return v + "%";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
axisLabel: "CPU loading",
|
||||
axisLabelUseCanvas: true,
|
||||
axisLabelFontSizePixels: 12,
|
||||
axisLabelFontFamily: 'Verdana, Arial',
|
||||
axisLabelPadding: 6
|
||||
}, {
|
||||
max: 5120,
|
||||
position: "right",
|
||||
axisLabel: "Disk",
|
||||
axisLabelUseCanvas: true,
|
||||
axisLabelFontSizePixels: 12,
|
||||
axisLabelFontFamily: 'Verdana, Arial',
|
||||
axisLabelPadding: 6
|
||||
}
|
||||
],
|
||||
legend: {
|
||||
noColumns: 0,
|
||||
position: "nw"
|
||||
},
|
||||
grid: {
|
||||
backgroundColor: {colors: ["#ffffff", "#EDF5FF"]}
|
||||
}
|
||||
};
|
||||
|
||||
function initData() {
|
||||
for (var i = 0; i < totalPoints; i++) {
|
||||
var temp = [now += updateInterval, 0];
|
||||
|
||||
cpu.push(temp);
|
||||
}
|
||||
}
|
||||
|
||||
function GetData() {
|
||||
|
||||
var data = {
|
||||
domain: $("#domain").text()
|
||||
};
|
||||
$.ajaxSetup({cache: false});
|
||||
|
||||
$.ajax({
|
||||
url: "/CloudLinux/getUsageData",
|
||||
dataType: 'json',
|
||||
success: update,
|
||||
type: "POST",
|
||||
headers: {'X-CSRFToken': getCookie('csrftoken')},
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(data), // Our valid JSON string
|
||||
error: function () {
|
||||
setTimeout(GetData, updateInterval);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var temp;
|
||||
|
||||
function update(_data) {
|
||||
cpu.shift();
|
||||
|
||||
now += updateInterval;
|
||||
|
||||
temp = [now, _data.cpu];
|
||||
cpu.push(temp);
|
||||
|
||||
|
||||
dataset = [
|
||||
{label: "CPU:" + _data.cpu + "%", data: cpu, lines: {fill: true, lineWidth: 1.2}, color: "#00FF00"}
|
||||
];
|
||||
|
||||
$.plot($("#flot-placeholder1"), dataset, options);
|
||||
setTimeout(GetData, updateInterval);
|
||||
}
|
||||
|
||||
// Memory Usage of User
|
||||
|
||||
var memory = [];
|
||||
var datasetMemory;
|
||||
var totalPointsMemory = 100;
|
||||
var updateIntervalMemory = 1000;
|
||||
var nowMemory = new Date().getTime();
|
||||
|
||||
var optionsMemory = {
|
||||
series: {
|
||||
lines: {
|
||||
lineWidth: 1.2
|
||||
},
|
||||
bars: {
|
||||
align: "center",
|
||||
fillColor: {colors: [{opacity: 1}, {opacity: 1}]},
|
||||
barWidth: 500,
|
||||
lineWidth: 1
|
||||
}
|
||||
},
|
||||
xaxis: {
|
||||
mode: "time",
|
||||
tickSize: [5, "second"],
|
||||
tickFormatter: function (v, axis) {
|
||||
var date = new Date(v);
|
||||
|
||||
if (date.getSeconds() % 20 == 0) {
|
||||
var hours = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
|
||||
var minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
|
||||
var seconds = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
|
||||
|
||||
return hours + ":" + minutes + ":" + seconds;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
axisLabel: "Time",
|
||||
axisLabelUseCanvas: true,
|
||||
axisLabelFontSizePixels: 12,
|
||||
axisLabelFontFamily: 'Verdana, Arial',
|
||||
axisLabelPadding: 10
|
||||
},
|
||||
yaxes: [
|
||||
{
|
||||
min: 0,
|
||||
max: $scope.memory,
|
||||
tickSize: 5,
|
||||
tickFormatter: function (v, axis) {
|
||||
if (v % 10 == 0) {
|
||||
return v + "MB";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
axisLabel: "CPU loading",
|
||||
axisLabelUseCanvas: true,
|
||||
axisLabelFontSizePixels: 12,
|
||||
axisLabelFontFamily: 'Verdana, Arial',
|
||||
axisLabelPadding: 6
|
||||
}, {
|
||||
max: 5120,
|
||||
position: "right",
|
||||
axisLabel: "Disk",
|
||||
axisLabelUseCanvas: true,
|
||||
axisLabelFontSizePixels: 12,
|
||||
axisLabelFontFamily: 'Verdana, Arial',
|
||||
axisLabelPadding: 6
|
||||
}
|
||||
],
|
||||
legend: {
|
||||
noColumns: 0,
|
||||
position: "nw"
|
||||
},
|
||||
grid: {
|
||||
backgroundColor: {colors: ["#ffffff", "#EDF5FF"]}
|
||||
}
|
||||
};
|
||||
|
||||
function initDataMemory() {
|
||||
for (var i = 0; i < totalPointsMemory; i++) {
|
||||
var temp = [nowMemory += updateIntervalMemory, 0];
|
||||
|
||||
memory.push(temp);
|
||||
}
|
||||
}
|
||||
|
||||
function GetDataMemory() {
|
||||
|
||||
var data = {
|
||||
domain: $("#domain").text(),
|
||||
type: 'memory'
|
||||
};
|
||||
$.ajaxSetup({cache: false});
|
||||
|
||||
$.ajax({
|
||||
url: "/CloudLinux/getUsageData",
|
||||
dataType: 'json',
|
||||
headers: {'X-CSRFToken': getCookie('csrftoken')},
|
||||
success: updateMemory,
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(data), // Our valid JSON string
|
||||
error: function () {
|
||||
setTimeout(GetDataMemory, updateIntervalMemory);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var tempMemory;
|
||||
|
||||
function updateMemory(_data) {
|
||||
memory.shift();
|
||||
|
||||
nowMemory += updateIntervalMemory;
|
||||
|
||||
tempMemory = [nowMemory, _data.memory];
|
||||
memory.push(tempMemory);
|
||||
|
||||
|
||||
datasetMemory = [
|
||||
{
|
||||
label: "Memory:" + _data.memory + "MB",
|
||||
data: memory,
|
||||
lines: {fill: true, lineWidth: 1.2},
|
||||
color: "#00FF00"
|
||||
}
|
||||
];
|
||||
|
||||
$.plot($("#memoryUsage"), datasetMemory, optionsMemory);
|
||||
setTimeout(GetDataMemory, updateIntervalMemory);
|
||||
}
|
||||
|
||||
// Disk Usage
|
||||
|
||||
var readRate = [], writeRate = [];
|
||||
var datasetDisk;
|
||||
var totalPointsDisk = 100;
|
||||
var updateIntervalDisk = 5000;
|
||||
var now = new Date().getTime();
|
||||
|
||||
var optionsDisk = {
|
||||
series: {
|
||||
lines: {
|
||||
lineWidth: 1.2
|
||||
},
|
||||
bars: {
|
||||
align: "center",
|
||||
fillColor: {colors: [{opacity: 1}, {opacity: 1}]},
|
||||
barWidth: 500,
|
||||
lineWidth: 1
|
||||
}
|
||||
},
|
||||
xaxis: {
|
||||
mode: "time",
|
||||
tickSize: [30, "second"],
|
||||
tickFormatter: function (v, axis) {
|
||||
var date = new Date(v);
|
||||
|
||||
if (date.getSeconds() % 20 == 0) {
|
||||
var hours = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
|
||||
var minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
|
||||
var seconds = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
|
||||
|
||||
return hours + ":" + minutes + ":" + seconds;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
axisLabel: "Time",
|
||||
axisLabelUseCanvas: true,
|
||||
axisLabelFontSizePixels: 12,
|
||||
axisLabelFontFamily: 'Verdana, Arial',
|
||||
axisLabelPadding: 10
|
||||
},
|
||||
yaxes: [
|
||||
{
|
||||
min: 0,
|
||||
max: $scope.networkSpeed,
|
||||
tickSize: 5,
|
||||
tickFormatter: function (v, axis) {
|
||||
if (v % 10 == 0) {
|
||||
return v + "mb/sec";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
axisLabel: "CPU loading",
|
||||
axisLabelUseCanvas: true,
|
||||
axisLabelFontSizePixels: 12,
|
||||
axisLabelFontFamily: 'Verdana, Arial',
|
||||
axisLabelPadding: 6
|
||||
}, {
|
||||
max: 5120,
|
||||
position: "right",
|
||||
axisLabel: "Disk",
|
||||
axisLabelUseCanvas: true,
|
||||
axisLabelFontSizePixels: 12,
|
||||
axisLabelFontFamily: 'Verdana, Arial',
|
||||
axisLabelPadding: 6
|
||||
}
|
||||
],
|
||||
legend: {
|
||||
noColumns: 0,
|
||||
position: "nw"
|
||||
},
|
||||
grid: {
|
||||
backgroundColor: {colors: ["#ffffff", "#EDF5FF"]}
|
||||
}
|
||||
};
|
||||
|
||||
function initDataDisk() {
|
||||
for (var i = 0; i < totalPointsDisk; i++) {
|
||||
var temp = [now += updateIntervalDisk, 0];
|
||||
|
||||
readRate.push(temp);
|
||||
writeRate.push(temp);
|
||||
}
|
||||
}
|
||||
|
||||
function GetDataDisk() {
|
||||
|
||||
var data = {
|
||||
domain: $("#domain").text(),
|
||||
type: 'io'
|
||||
};
|
||||
|
||||
$.ajaxSetup({cache: false});
|
||||
|
||||
$.ajax({
|
||||
url: "/CloudLinux/getUsageData",
|
||||
dataType: 'json',
|
||||
headers: {'X-CSRFToken': getCookie('csrftoken')},
|
||||
success: updateDisk,
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(data), // Our valid JSON string
|
||||
error: function () {
|
||||
setTimeout(GetDataMemory, updateIntervalMemory);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var tempDisk;
|
||||
|
||||
function updateDisk(_data) {
|
||||
readRate.shift();
|
||||
writeRate.shift();
|
||||
|
||||
now += updateIntervalDisk;
|
||||
|
||||
tempDisk = [now, _data.readRate];
|
||||
readRate.push(tempDisk);
|
||||
|
||||
tempDisk = [now, _data.readRate];
|
||||
writeRate.push(tempDisk);
|
||||
|
||||
datasetDisk = [
|
||||
{
|
||||
label: "Read IO/s " + _data.readRate + " mb/s ",
|
||||
data: readRate,
|
||||
lines: {fill: true, lineWidth: 1.2},
|
||||
color: "#00FF00"
|
||||
},
|
||||
{
|
||||
label: "Write IO/s " + _data.writeRate + " mb/s ",
|
||||
data: writeRate,
|
||||
lines: {lineWidth: 1.2},
|
||||
color: "#FF0000"
|
||||
}
|
||||
];
|
||||
|
||||
$.plot($("#diskUsage"), datasetDisk, optionsDisk);
|
||||
setTimeout(GetDataDisk, updateIntervalDisk);
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
// Report Memory Usage
|
||||
|
||||
initDataMemory();
|
||||
|
||||
datasetMemory = [
|
||||
{label: "Memory", data: memory, lines: {fill: true, lineWidth: 1.2}, color: "#00FF00"}
|
||||
];
|
||||
|
||||
$.plot($("#memoryUsage"), datasetMemory, optionsMemory);
|
||||
setTimeout(GetDataMemory, updateIntervalMemory);
|
||||
|
||||
// Report CPU Usage
|
||||
|
||||
initData();
|
||||
|
||||
dataset = [
|
||||
{label: "CPU", data: cpu, lines: {fill: true, lineWidth: 1.2}, color: "#00FF00"}
|
||||
];
|
||||
|
||||
$.plot($("#flot-placeholder1"), dataset, options);
|
||||
setTimeout(GetData, updateInterval);
|
||||
|
||||
// Report Disk Usage
|
||||
|
||||
initDataDisk();
|
||||
|
||||
datasetDisk = [
|
||||
{label: "Read IO/s: ", data: readRate, lines: {fill: true, lineWidth: 1.2}, color: "#00FF00"},
|
||||
{label: "Write IO/s: ", data: writeRate, color: "#0044FF", bars: {show: true}, yaxis: 2}
|
||||
];
|
||||
|
||||
$.plot($("#diskUsage"), datasetDisk, optionsDisk);
|
||||
setTimeout(GetDataDisk, updateIntervalDisk);
|
||||
});
|
||||
});
|
||||
37
CLManager/templates/CLManager/cloudLinux.html
Normal file
37
CLManager/templates/CLManager/cloudLinux.html
Normal file
@@ -0,0 +1,37 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "CloudLinux - 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 "CloudLinux" %}</h2>
|
||||
<p>{% trans "Access LVEManager" %}</p>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "CloudLinux" %}
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
<p>{% trans "CloudLinux is now integrated via their new API. You can manage CageFS and Package limits directly from LVEManager by clicking below. You can use your server root credentials to access LVEManager." %}</p>
|
||||
<br>
|
||||
<a target="_blank" href="http://{{ ipAddress }}:9000">
|
||||
<button class="btn btn-primary">Access Now
|
||||
</button>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
146
CLManager/templates/CLManager/createPackage.html
Normal file
146
CLManager/templates/CLManager/createPackage.html
Normal file
@@ -0,0 +1,146 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Create Cloud Linux Package - CyberPanel" %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
<div class="container">
|
||||
<div id="page-title">
|
||||
<h2>{% trans "Create CloudLinux Package." %}</h2>
|
||||
<p>{% trans "Each CloudLinux package have one associated (owner) CyberPanel package. During website creation associated CloudLinux package will be assigned to website user." %}</p>
|
||||
</div>
|
||||
<div ng-controller="createCLPackage" class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="content-box-header">
|
||||
{% trans "Create Package" %} <img ng-hide="cyberPanelLoading" src="{% static 'images/loading.gif' %}">
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
|
||||
<form action="/" class="form-horizontal bordered-row panel-body">
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Package" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select ng-change="toggleView()" ng-model="selectedPackage" class="form-control">
|
||||
{% for items in packList %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!------ Modification form that appears after a click --------------->
|
||||
|
||||
<div ng-hide="modifyPackageForm">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Package Name" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="name" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "SPEED" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="SPEED" required>
|
||||
</div>
|
||||
<div class="current-pack ng-binding">Ex 100%</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "VMEM" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="VMEM" required>
|
||||
</div>
|
||||
<div class="current-pack ng-binding">Ex 256m or 1G</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "PMEM" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="PMEM" required>
|
||||
</div>
|
||||
<div class="current-pack ng-binding">Ex 256m or 1G</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "IO" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="IO" required>
|
||||
</div>
|
||||
<div class="current-pack ng-binding">Ex 1024</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "IOPS" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="IOPS" required>
|
||||
</div>
|
||||
<div class="current-pack ng-binding">Ex 1024</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "EP" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="EP" required>
|
||||
</div>
|
||||
<div class="current-pack ng-binding">Ex 10</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "NPROC" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="NPROC" required>
|
||||
</div>
|
||||
<div class="current-pack ng-binding">Ex 10</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "INODES soft" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="INODESsoft" required>
|
||||
</div>
|
||||
<div class="current-pack ng-binding">Ex 1024</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "INODES hard" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="INODEShard" required>
|
||||
</div>
|
||||
<div class="current-pack ng-binding">Ex 1024</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<!------ Modification form that appears after a click --------------->
|
||||
|
||||
|
||||
<div ng-hide="modifyPackageForm" class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-click="createPackage()"
|
||||
class="btn btn-primary btn-lg ">{% trans "Create Package" %}</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
236
CLManager/templates/CLManager/listPackages.html
Normal file
236
CLManager/templates/CLManager/listPackages.html
Normal file
@@ -0,0 +1,236 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Manage CloudLinux Packages - CyberPanel" %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
|
||||
<div ng-controller="listCloudLinuxPackages" class="container">
|
||||
|
||||
<div id="page-title">
|
||||
<h2 id="domainNamePage">{% trans "Manage CloudLinux Packages" %}</h2>
|
||||
<p>{% trans "Manage/Delete CloudLinux Packages." %}</p>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-body">
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped table-bordered"
|
||||
id="datatable-example">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name<img ng-hide="cyberPanelLoading" src="/static/images/loading.gif"></th>
|
||||
<th>SPEED</th>
|
||||
<th>VMEM</th>
|
||||
<th>PMEM</th>
|
||||
<th>IO</th>
|
||||
<th>IOPS</th>
|
||||
<th>EP</th>
|
||||
<th>NPROC</th>
|
||||
<th>INODES soft</th>
|
||||
<th>INODES hard</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr ng-repeat="pack in packages track by $index">
|
||||
<td ng-bind="pack.name"></td>
|
||||
<td ng-bind="pack.SPEED"></td>
|
||||
<td ng-bind="pack.VMEM"></td>
|
||||
<td ng-bind="pack.PMEM"></td>
|
||||
<td ng-bind="pack.IO"></td>
|
||||
<td ng-bind="pack.IOPS"></td>
|
||||
<td ng-bind="pack.EP"></td>
|
||||
<td ng-bind="pack.NPROC"></td>
|
||||
<td ng-bind="pack.inodessoft"></td>
|
||||
<td ng-bind="pack.inodeshard"></td>
|
||||
<td>
|
||||
<a ng-click='deleteCLPackage(pack.name)'
|
||||
class="btn btn-border btn-alt border-red btn-link font-red"
|
||||
title=""><span>Delete</span></a>
|
||||
<a ng-click="populatePackage(pack.name, pack.SPEED, pack.VMEM, pack.PMEM, pack.IO, pack.IOPS, pack.EP, pack.NPROC, pack.inodessoft, pack.inodeshard)" data-toggle="modal" data-target="#settings" ng-click='deleteCLPackage()'
|
||||
class="btn btn-border btn-alt border-green btn-link font-green"
|
||||
title=""><span>Edit</span></a>
|
||||
<div id="settings" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×
|
||||
</button>
|
||||
<h4 class="modal-title">Edit Package
|
||||
<img id="containerSettingLoading" src="/static/images/loading.gif"
|
||||
style="display: none;">
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<form name="containerSettingsForm" action="/" class="form-horizontal">
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Name" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="name" type="text" class="form-control"
|
||||
ng-model="name" readonly>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "SPEED" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="SPEED" type="text" class="form-control"
|
||||
ng-model="$parent.SPEED" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "VMEM" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="VMEM" type="text" class="form-control"
|
||||
ng-model="$parent.VMEM" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "PMEM" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="PMEM" type="text" class="form-control"
|
||||
ng-model="$parent.PMEM" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "IO" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="IO" type="text" class="form-control"
|
||||
ng-model="$parent.IO" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "IOPS" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="IOPS" type="text" class="form-control"
|
||||
ng-model="$parent.IOPS" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "EP" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="EP" type="text" class="form-control"
|
||||
ng-model="$parent.EP" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "NPROC" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="NPROC" type="text" class="form-control"
|
||||
ng-model="$parent.NPROC" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "INODES soft" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="inodessoft" type="text" class="form-control"
|
||||
ng-model="$parent.inodessoft" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "INODES hard" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="inodeshard" type="text" class="form-control"
|
||||
ng-model="$parent.inodeshard" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" ng-disabled="savingSettings"
|
||||
class="btn btn-primary"
|
||||
ng-click="saveSettings()" data-dismiss="modal">Save
|
||||
</button>
|
||||
<button type="button" ng-disabled="savingSettings"
|
||||
class="btn btn-default" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div id="listFail" class="alert alert-danger">
|
||||
<p>{% trans "Cannot list websites. Error message:" %} {$ errorMessage $}</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-4 col-sm-offset-8">
|
||||
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
|
||||
<li ng-repeat="page in pagination" ng-click="getFurtherWebsitesFromDB($index+1)"
|
||||
id="webPages"><a
|
||||
href="">{$ $index + 1 $}</a></li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
117
CLManager/templates/CLManager/listWebsites.html
Normal file
117
CLManager/templates/CLManager/listWebsites.html
Normal file
@@ -0,0 +1,117 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "CageFS - CyberPanel" %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
|
||||
<div ng-controller="listWebsitesCage" class="container">
|
||||
|
||||
<div id="page-title">
|
||||
<h2 id="domainNamePage">{% trans "List Websites" %}</h2>
|
||||
<p>{% trans "Enable/Disable and view CageFS status for websites." %}</p>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-body">
|
||||
<div style="padding-bottom: 0px; padding-top: 15px;" class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-6">
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
<div class="content-box remove-border clearfix text-center">
|
||||
|
||||
<a class="btn btn-primary" href="#" title="">
|
||||
<span>{% trans "Default: " %}
|
||||
<b>{$ default $}</b></span>
|
||||
</a>
|
||||
|
||||
<a href="#" ng-click="enableOrDisable(0, 0, 0, 1)"
|
||||
class="btn btn-border btn-alt border-green btn-link font-green"
|
||||
title=""><span>Toggle Default</span></a>
|
||||
|
||||
|
||||
<a href="#" ng-click="enableOrDisable(0, 1, 1, 0)" class="btn btn-success" title="Enable All">
|
||||
|
||||
<i class="fa fa-play btn-icon"></i>
|
||||
</a>
|
||||
<a href="#" ng-click="enableOrDisable(0, 1, 0, 0)" class="btn btn-warning" title="Disable All">
|
||||
|
||||
<i class="fa fa-pause btn-icon"></i>
|
||||
</a>
|
||||
|
||||
<a href="#" ng-click="refreshStatus()" class="btn btn-info" title="Refresh Status">
|
||||
<i class="fa fa-refresh btn-icon"></i>
|
||||
</a>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped table-bordered"
|
||||
id="datatable-example">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domain <img ng-hide="cyberPanelLoading" src="/static/images/loading.gif"></th>
|
||||
<th>User</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr ng-repeat="web in WebSitesList track by $index">
|
||||
<td ng-bind="web.domain"></td>
|
||||
<td ng-bind="web.externalApp"></td>
|
||||
<td>
|
||||
<a ng-click="enableOrDisable(web.domain, 0, 0, 0)" ng-hide="web.status==0"
|
||||
class="btn btn-border btn-alt border-red btn-link font-red"
|
||||
title=""><span>Disable</span></a>
|
||||
<a ng-click="enableOrDisable(web.domain, 0, 1, 0)" ng-hide="web.status==1"
|
||||
class="btn btn-border btn-alt border-green btn-link font-green"
|
||||
title=""><span>Enable</span></a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div id="listFail" class="alert alert-danger">
|
||||
<p>{% trans "Cannot list websites. Error message:" %} {$ errorMessage $}</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-4 col-sm-offset-8">
|
||||
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
|
||||
<li ng-repeat="page in pagination" ng-click="getFurtherWebsitesFromDB($index+1)"
|
||||
id="webPages"><a
|
||||
href="">{$ $index + 1 $}</a></li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
87
CLManager/templates/CLManager/monitorUsage.html
Normal file
87
CLManager/templates/CLManager/monitorUsage.html
Normal file
@@ -0,0 +1,87 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Monitor Usage - CyberPanel" %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div id="page-title">
|
||||
<h2 id="domainNamePage">{% trans "List Websites" %}</h2>
|
||||
<p>{% trans "Monitor usage of your websites." %}</p>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Websites" %}
|
||||
</h3>
|
||||
<div ng-controller="listWebsites" class="example-box-wrapper">
|
||||
|
||||
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped table-bordered"
|
||||
id="datatable-example">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
<th>Launch</th>
|
||||
<th>IP Address</th>
|
||||
<th>Package</th>
|
||||
<th>Owner</th>
|
||||
<th>State</th>
|
||||
<th>Email</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr ng-repeat="web in WebSitesList track by $index">
|
||||
<td ng-bind="web.domain"></td>
|
||||
<td><a href="/CloudLinux/manage/{$ web.domain $}"><img width="30px" height="30"
|
||||
class="center-block"
|
||||
src="{% static 'baseTemplate/assets/image-resources/webPanel.png' %}"></a>
|
||||
</td>
|
||||
<td ng-bind="web.ipAddress"></td>
|
||||
<td ng-bind="web.package"></td>
|
||||
<td ng-bind="web.admin"></td>
|
||||
<td ng-bind="web.state"></td>
|
||||
<td ng-bind="web.adminEmail"></td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div id="listFail" class="alert alert-danger">
|
||||
<p>{% trans "Cannot list websites. Error message:" %} {$ errorMessage $}</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-4 col-sm-offset-8">
|
||||
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
|
||||
<li ng-repeat="page in pagination" ng-click="getFurtherWebsitesFromDB($index+1)" id="webPages"><a
|
||||
href="">{$ $index + 1 $}</a></li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
67
CLManager/templates/CLManager/notAvailable.html
Normal file
67
CLManager/templates/CLManager/notAvailable.html
Normal file
@@ -0,0 +1,67 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Not available - 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 "Not available" %}</h2>
|
||||
<p>{% trans "Either CageFS is not installed or you are not on CloudLinux OS." %}</p>
|
||||
</div>
|
||||
|
||||
{% if not CL %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="alert alert-danger">
|
||||
<p>{% trans "CloudLinux is not installed on your server." %} <a target="_blank"
|
||||
href="https://community.cyberpanel.net/t/1-convert-cyberpanel-to-cloudlinux-os-and-install-cagefs/174">Click
|
||||
Here</a> {% trans " for conversion details." %}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
|
||||
<div ng-controller="installCageFS" class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Activate Now" %} <img ng-hide="installDockerStatus"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
<p>{% trans "CloudLinux is installed, but not activated." %}</p>
|
||||
<!------ LSWS Switch box ----------------->
|
||||
|
||||
<div style="margin-top: 2%" ng-hide="installBoxGen" class="col-md-12">
|
||||
|
||||
<form action="/" id="" class="form-horizontal bordered-row">
|
||||
<div class="form-group">
|
||||
<div style="margin-top: 2%;" class="col-sm-12">
|
||||
<textarea ng-model="requestData" rows="15"
|
||||
class="form-control">{{ requestData }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<!----- LSWS Switch box ----------------->
|
||||
<br>
|
||||
<button ng-hide="dockerInstallBTN" class="btn btn-primary" ng-click="submitCageFSInstall()">Activate Now</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
52
CLManager/templates/CLManager/websiteContainerLimit.html
Normal file
52
CLManager/templates/CLManager/websiteContainerLimit.html
Normal file
@@ -0,0 +1,52 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{{ domain }}{% trans " usage - CyberPanel" %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
|
||||
<div ng-controller="websiteContainerLimitCL" class="container">
|
||||
|
||||
<div id="page-title">
|
||||
<h2 id="domainNamePage">{% trans "Usage" %}</h2>
|
||||
<p>{% trans "View CPU, Memory and Disk usage for " %} <span id="domain">{{ domain }}</span></p>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-body">
|
||||
<h2 class="title-hero">
|
||||
{% trans "CPU Usage of" %} {{ domain }}
|
||||
</h2>
|
||||
<div class="example-box-wrapper">
|
||||
<div id="flot-placeholder1" style="width:auto;height:300px"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<h2 class="title-hero">
|
||||
{% trans "Memory Usage of" %} {{ domain }}
|
||||
</h2>
|
||||
<div class="example-box-wrapper">
|
||||
<div id="memoryUsage" style="width:auto;height:300px"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<h2 class="title-hero">
|
||||
{% trans "Disk Usage of" %} {{ domain }}
|
||||
</h2>
|
||||
<div class="example-box-wrapper">
|
||||
<div id="diskUsage" style="width:auto;height:300px"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
6
CLManager/tests.py
Normal file
6
CLManager/tests.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
19
CLManager/urls.py
Normal file
19
CLManager/urls.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from django.urls import re_path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^CreatePackage$', views.CreatePackage, name='CreatePackageCL'),
|
||||
re_path(r'^listPackages$', views.listPackages, name='listPackagesCL'),
|
||||
re_path(r'^monitorUsage$', views.monitorUsage, name='monitorUsage'),
|
||||
re_path(r'^CageFS$', views.CageFS, name='CageFS'),
|
||||
re_path(r'^submitCageFSInstall$', views.submitCageFSInstall, name='submitCageFSInstall'),
|
||||
|
||||
# re_path(r'^submitWebsiteListing$', views.getFurtherAccounts, name='submitWebsiteListing'),
|
||||
# re_path(r'^enableOrDisable$', views.enableOrDisable, name='enableOrDisable'),
|
||||
# re_path(r'^submitCreatePackage$', views.submitCreatePackage, name='submitCreatePackageCL'),
|
||||
# re_path(r'^fetchPackages$', views.fetchPackages, name='fetchPackagesCL'),
|
||||
# re_path(r'^deleteCLPackage$', views.deleteCLPackage, name='deleteCLPackage'),
|
||||
# re_path(r'^saveSettings$', views.saveSettings, name='saveSettings'),
|
||||
# re_path(r'^manage/(?P<domain>(.*))$', views.websiteContainerLimit, name='websiteContainerLimitCL'),
|
||||
# re_path(r'^getUsageData$', views.getUsageData, name='getUsageData'),
|
||||
]
|
||||
357
CLManager/views.py
Normal file
357
CLManager/views.py
Normal file
@@ -0,0 +1,357 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from django.shortcuts import redirect, HttpResponse
|
||||
from loginSystem.views import loadLoginPage
|
||||
from plogical.acl import ACLManager
|
||||
from .CLManagerMain import CLManagerMain
|
||||
import json
|
||||
from websiteFunctions.models import Websites
|
||||
from plogical.processUtilities import ProcessUtilities
|
||||
import os
|
||||
from packages.models import Package
|
||||
from .models import CLPackages
|
||||
import subprocess
|
||||
import multiprocessing
|
||||
import pwd
|
||||
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
|
||||
# Create your views here.
|
||||
|
||||
def CageFS(request):
|
||||
try:
|
||||
templateName = 'CLManager/listWebsites.html'
|
||||
c = CLManagerMain(request, templateName)
|
||||
return c.renderC()
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
def submitCageFSInstall(request):
|
||||
try:
|
||||
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
c = CLManagerMain(request, None, 'submitCageFSInstall')
|
||||
c.start()
|
||||
|
||||
data_ret = {'status': 1, 'error_message': 'None'}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
except BaseException as msg:
|
||||
data_ret = {'status': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def getFurtherAccounts(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
wm = CLManagerMain()
|
||||
return wm.getFurtherAccounts(userID, json.loads(request.body))
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
def enableOrDisable(request):
|
||||
try:
|
||||
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
data = json.loads(request.body)
|
||||
|
||||
if data['toggle'] == 1:
|
||||
cageFSPath = '/home/cyberpanel/cagefs'
|
||||
if os.path.exists(cageFSPath):
|
||||
os.remove(cageFSPath)
|
||||
else:
|
||||
writeToFile = open(cageFSPath, 'w')
|
||||
writeToFile.writelines('enable')
|
||||
writeToFile.close()
|
||||
|
||||
data_ret = {'status': 1, 'error_message': 'None', 'success': 'Default status successfully changed changed.'}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
if data['all'] == 0:
|
||||
if data['mode'] == 1:
|
||||
website = Websites.objects.get(domain=data['domain'])
|
||||
command = '/usr/sbin/cagefsctl --enable %s' % (website.externalApp)
|
||||
else:
|
||||
website = Websites.objects.get(domain=data['domain'])
|
||||
command = '/usr/sbin/cagefsctl --disable %s' % (website.externalApp)
|
||||
|
||||
ProcessUtilities.executioner(command)
|
||||
data_ret = {'status': 1, 'error_message': 'None', 'success': 'Changes successfully applied.'}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
else:
|
||||
c = CLManagerMain(request, None, 'enableOrDisable', data)
|
||||
c.start()
|
||||
|
||||
data_ret = {'status': 1, 'error_message': 'None', 'success': 'Job started in background, refresh in few seconds to see the status.'}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
|
||||
|
||||
except BaseException as msg:
|
||||
data_ret = {'status': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def CreatePackage(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
templateName = 'CLManager/createPackage.html'
|
||||
packageList = ACLManager.loadPackages(userID, currentACL)
|
||||
data = {}
|
||||
data['packList'] = packageList
|
||||
c = CLManagerMain(request, templateName, None, data)
|
||||
return c.renderC()
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
def submitCreatePackage(request):
|
||||
try:
|
||||
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
data = json.loads(request.body)
|
||||
|
||||
selectedPackage = data['selectedPackage']
|
||||
|
||||
package = Package.objects.get(packageName=selectedPackage)
|
||||
|
||||
if package.clpackages_set.all().count() == 1:
|
||||
data_ret = {'status': 0, 'error_message': 'This package already have one associated CloudLinux Package.'}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
name = data['name']
|
||||
SPEED = data['SPEED']
|
||||
VMEM = data['VMEM']
|
||||
PMEM = data['PMEM']
|
||||
IO = data['IO']
|
||||
IOPS = data['IOPS']
|
||||
EP = data['EP']
|
||||
NPROC = data['NPROC']
|
||||
INODESsoft = data['INODESsoft']
|
||||
INODEShard = data['INODEShard']
|
||||
|
||||
clPackage = CLPackages(name=name, owner=package, speed=SPEED, vmem=VMEM, pmem=PMEM, io=IO, iops=IOPS, ep=EP, nproc=NPROC, inodessoft=INODESsoft, inodeshard=INODEShard)
|
||||
clPackage.save()
|
||||
|
||||
command = 'sudo lvectl package-set %s --speed=%s --pmem=%s --io=%s --nproc=%s --iops=%s --vmem=%s --ep=%s' % (name, SPEED, PMEM, IO, NPROC, IOPS, VMEM, EP)
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
command = 'sudo lvectl apply all'
|
||||
ProcessUtilities.popenExecutioner(command)
|
||||
|
||||
data_ret = {'status': 1}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
|
||||
except BaseException as msg:
|
||||
data_ret = {'status': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def listPackages(request):
|
||||
try:
|
||||
templateName = 'CLManager/listPackages.html'
|
||||
c = CLManagerMain(request, templateName)
|
||||
return c.renderC()
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
def fetchPackages(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
wm = CLManagerMain()
|
||||
return wm.fetchPackages(ACLManager.loadedACL(userID))
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
def deleteCLPackage(request):
|
||||
try:
|
||||
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
data = json.loads(request.body)
|
||||
|
||||
name = data['name']
|
||||
|
||||
clPackage = CLPackages.objects.get(name=name)
|
||||
clPackage.delete()
|
||||
|
||||
data_ret = {'status': 1}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
|
||||
except BaseException as msg:
|
||||
data_ret = {'status': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def saveSettings(request):
|
||||
try:
|
||||
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
data = json.loads(request.body)
|
||||
|
||||
name = data['name']
|
||||
SPEED = data['SPEED']
|
||||
VMEM = data['VMEM']
|
||||
PMEM = data['PMEM']
|
||||
IO = data['IO']
|
||||
IOPS = data['IOPS']
|
||||
EP = data['EP']
|
||||
NPROC = data['NPROC']
|
||||
INODESsoft = data['INODESsoft']
|
||||
INODEShard = data['INODEShard']
|
||||
|
||||
clPackage = CLPackages.objects.get(name=name)
|
||||
clPackage.speed = SPEED
|
||||
clPackage.vmem = VMEM
|
||||
clPackage.pmem = PMEM
|
||||
clPackage.io = IO
|
||||
clPackage.iops = IOPS
|
||||
clPackage.ep = EP
|
||||
clPackage.nproc = NPROC
|
||||
clPackage.inodessoft = INODESsoft
|
||||
clPackage.inodeshard = INODEShard
|
||||
clPackage.save()
|
||||
|
||||
command = 'sudo lvectl package-set %s --speed=%s --pmem=%s --io=%s --nproc=%s --iops=%s --vmem=%s --ep=%s' % (
|
||||
name, SPEED, PMEM, IO, NPROC, IOPS, VMEM, EP)
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
command = 'sudo lvectl apply all'
|
||||
ProcessUtilities.popenExecutioner(command)
|
||||
|
||||
data_ret = {'status': 1}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
|
||||
except BaseException as msg:
|
||||
data_ret = {'status': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def monitorUsage(request):
|
||||
try:
|
||||
templateName = 'CLManager/monitorUsage.html'
|
||||
c = CLManagerMain(request, templateName)
|
||||
return c.renderC()
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
def websiteContainerLimit(request, domain):
|
||||
try:
|
||||
templateName = 'CLManager/websiteContainerLimit.html'
|
||||
data = {}
|
||||
data['domain'] = domain
|
||||
c = CLManagerMain(request, templateName, None, data)
|
||||
return c.renderC()
|
||||
except KeyError:
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
def getUsageData(request):
|
||||
try:
|
||||
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
data = json.loads(request.body)
|
||||
domain = data['domain']
|
||||
website = Websites.objects.get(domain=domain)
|
||||
uid = pwd.getpwnam(website.externalApp).pw_uid
|
||||
|
||||
try:
|
||||
type = data['type']
|
||||
finalData = {}
|
||||
finalData['status'] = 1
|
||||
|
||||
try:
|
||||
if type == 'memory':
|
||||
|
||||
command = 'sudo lveps -o id:10,mem:10'
|
||||
output = ProcessUtilities.outputExecutioner(command).splitlines()
|
||||
for items in output:
|
||||
if items.find(website.externalApp) > -1:
|
||||
finalData['memory'] = int(items.split(' ')[-1])
|
||||
break
|
||||
|
||||
elif type == 'io':
|
||||
|
||||
finalData['readRate'] = 0
|
||||
finalData['writeRate'] = 0
|
||||
|
||||
command = 'sudo lveps -o id:10,iops:10'
|
||||
output = ProcessUtilities.outputExecutioner(command).splitlines()
|
||||
for items in output:
|
||||
if items.find(website.externalApp) > -1:
|
||||
finalData['readRate'] = int(items.split(' ')[-1])
|
||||
break
|
||||
|
||||
except:
|
||||
finalData['memory'] = '0'
|
||||
finalData['readRate'] = 0
|
||||
finalData['writeRate'] = 0
|
||||
except:
|
||||
|
||||
finalData = {}
|
||||
finalData['status'] = 1
|
||||
|
||||
command = 'sudo lveps -o id:10,cpu:10 -d'
|
||||
output = ProcessUtilities.outputExecutioner(command).splitlines()
|
||||
|
||||
for items in output:
|
||||
if items.find(website.externalApp) > -1:
|
||||
finalData['cpu'] = int(items.split(' ')[-1].rstrip('%'))
|
||||
break
|
||||
|
||||
final_json = json.dumps(finalData)
|
||||
return HttpResponse(final_json)
|
||||
|
||||
except BaseException as msg:
|
||||
data_ret = {'status': 0, 'error_message': str(msg), 'cpu': 0, 'memory':0}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
17
CLScript/CLMain.py
Normal file
17
CLScript/CLMain.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import json
|
||||
|
||||
class CLMain():
|
||||
def __init__(self):
|
||||
self.path = '/usr/local/CyberCP/version.txt'
|
||||
#versionInfo = json.loads(open(self.path, 'r').read())
|
||||
self.version = '2.4'
|
||||
self.build = '2'
|
||||
|
||||
ipFile = "/etc/cyberpanel/machineIP"
|
||||
f = open(ipFile)
|
||||
ipData = f.read()
|
||||
self.ipAddress = ipData.split('\n', 1)[0]
|
||||
|
||||
self.initialMeta = {
|
||||
"result": "ok"
|
||||
}
|
||||
85
CLScript/CloudLinuxAdmins.py
Normal file
85
CLScript/CloudLinuxAdmins.py
Normal file
@@ -0,0 +1,85 @@
|
||||
#!/usr/local/CyberCP/bin/python
|
||||
import sys
|
||||
import os.path
|
||||
import django
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
|
||||
try:
|
||||
django.setup()
|
||||
except:
|
||||
pass
|
||||
from loginSystem.models import Administrator, ACL
|
||||
import argparse
|
||||
import json
|
||||
from CLScript.CLMain import CLMain
|
||||
|
||||
|
||||
class CloudLinuxAdmins(CLMain):
|
||||
|
||||
def __init__(self, name, isMain):
|
||||
CLMain.__init__(self)
|
||||
self.name = name
|
||||
if isMain == 'true':
|
||||
self.isMain = 1
|
||||
else:
|
||||
self.isMain = 0
|
||||
|
||||
def listAll(self):
|
||||
users = []
|
||||
acl = ACL.objects.get(name='admin')
|
||||
for items in Administrator.objects.filter(acl=acl):
|
||||
|
||||
if items.userName == 'admin':
|
||||
isMain = True
|
||||
else:
|
||||
isMain = False
|
||||
|
||||
if self.isMain:
|
||||
if isMain == False:
|
||||
continue
|
||||
|
||||
if self.name != None:
|
||||
if self.name != items.userName:
|
||||
continue
|
||||
|
||||
user = {'name': items.userName,
|
||||
"locale_code": "EN_us",
|
||||
"unix_user": None,
|
||||
"email": items.email,
|
||||
"is_main": isMain
|
||||
}
|
||||
|
||||
users.append(user)
|
||||
|
||||
|
||||
## Add root users
|
||||
|
||||
admin = Administrator.objects.get(userName='admin')
|
||||
|
||||
user = {'name': 'root',
|
||||
"locale_code": "EN_us",
|
||||
"unix_user": 'root',
|
||||
"email": admin.email,
|
||||
"is_main": True
|
||||
}
|
||||
|
||||
##
|
||||
|
||||
users.append(user)
|
||||
|
||||
final = {'data': users, 'metadata': self.initialMeta}
|
||||
print(json.dumps(final))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='CyberPanel CloudLinux Manager')
|
||||
parser.add_argument('-n','--name', help='Owner')
|
||||
parser.add_argument('-m', '--is-main', help='Owner')
|
||||
|
||||
args = vars(parser.parse_args())
|
||||
|
||||
pi = CloudLinuxAdmins(args['name'], args['is_main'])
|
||||
try:
|
||||
pi.listAll()
|
||||
except:
|
||||
pi.listAll()
|
||||
25
CLScript/CloudLinuxDB.py
Normal file
25
CLScript/CloudLinuxDB.py
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/usr/local/CyberCP/bin/python
|
||||
import sys
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
import json
|
||||
from CLScript.CLMain import CLMain
|
||||
|
||||
|
||||
class PanelInfo(CLMain):
|
||||
def __init__(self):
|
||||
CLMain.__init__(self)
|
||||
|
||||
def emit(self):
|
||||
|
||||
initial = {
|
||||
"mysql": None
|
||||
}
|
||||
|
||||
final = {'data': initial, 'metadata': self.initialMeta}
|
||||
|
||||
print(json.dumps(final))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pi = PanelInfo()
|
||||
pi.emit()
|
||||
104
CLScript/CloudLinuxDomains.py
Normal file
104
CLScript/CloudLinuxDomains.py
Normal file
@@ -0,0 +1,104 @@
|
||||
#!/usr/local/CyberCP/bin/python
|
||||
import sys
|
||||
import os.path
|
||||
import django
|
||||
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
|
||||
try:
|
||||
django.setup()
|
||||
except:
|
||||
pass
|
||||
from websiteFunctions.models import Websites
|
||||
import argparse
|
||||
import json
|
||||
from CLScript.CLMain import CLMain
|
||||
|
||||
|
||||
class CloudLinuxDomains(CLMain):
|
||||
|
||||
def __init__(self, name, owner, with_php):
|
||||
CLMain.__init__(self)
|
||||
self.owner = owner
|
||||
self.name = name
|
||||
self.with_php = with_php
|
||||
|
||||
def listAll(self):
|
||||
data = {}
|
||||
|
||||
if self.owner != None:
|
||||
websites = Websites.objects.filter(externalApp=self.owner)
|
||||
else:
|
||||
websites = Websites.objects.all()
|
||||
|
||||
for webs in websites:
|
||||
if self.name != None:
|
||||
if self.name != webs.domain:
|
||||
continue
|
||||
|
||||
if self.with_php:
|
||||
completePathToConfigFile = f'/usr/local/lsws/conf/vhosts/{webs.domain}/vhost.conf'
|
||||
from plogical.phpUtilities import phpUtilities
|
||||
from managePHP.phpManager import PHPManager
|
||||
phpVersion = phpUtilities.WrapGetPHPVersionFromFileToGetVersionWithPHP(completePathToConfigFile)
|
||||
php = PHPManager.getPHPString(phpVersion)
|
||||
|
||||
data[webs.domain] = {
|
||||
"owner": webs.externalApp,
|
||||
"document_root": "/home/%s/public_html/" % (webs.domain),
|
||||
"is_main": True,
|
||||
"php": {
|
||||
"version": php[:2],
|
||||
"ini_path": f"/usr/local/lsws/lsphp{php[:2]}/etc/php.d",
|
||||
"is_native": False
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for webs in webs.childdomains_set.all():
|
||||
completePathToConfigFile = f'/usr/local/lsws/conf/vhosts/{webs.domain}/vhost.conf'
|
||||
from plogical.phpUtilities import phpUtilities
|
||||
from managePHP.phpManager import PHPManager
|
||||
phpVersion = phpUtilities.WrapGetPHPVersionFromFileToGetVersionWithPHP(completePathToConfigFile)
|
||||
php = PHPManager.getPHPString(phpVersion)
|
||||
|
||||
data[webs.domain] = {"owner": webs.master.externalApp,
|
||||
"document_root": webs.path,
|
||||
"is_main": False,
|
||||
"php": {
|
||||
"version": php[:2],
|
||||
"ini_path": f"/usr/local/lsws/lsphp{php[:2]}/etc/php.d",
|
||||
"is_native": False
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else:
|
||||
data[webs.domain] = {"owner": webs.externalApp,
|
||||
"document_root": "/home/%s/public_html/" % (webs.domain),
|
||||
"is_main": True}
|
||||
for webs in webs.childdomains_set.all():
|
||||
data[webs.domain] = {"owner": webs.master.externalApp,
|
||||
"document_root": webs.path,
|
||||
"is_main": False}
|
||||
|
||||
final = {'data': data, 'metadata': self.initialMeta}
|
||||
print(json.dumps(final))
|
||||
|
||||
|
||||
import argparse
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='CyberPanel CloudLinux Manager')
|
||||
parser.add_argument('-o', '--owner', help='Owner')
|
||||
parser.add_argument('-n', '--name', help='Owner')
|
||||
parser.add_argument('-p', '--with-php', action='store_true', help='False (X-Ray support only)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Assuming CloudLinuxDomains class exists
|
||||
pi = CloudLinuxDomains(args.name, args.owner, args.with_php)
|
||||
try:
|
||||
pi.listAll()
|
||||
except:
|
||||
pi.listAll()
|
||||
53
CLScript/CloudLinuxPackages.py
Normal file
53
CLScript/CloudLinuxPackages.py
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/usr/local/CyberCP/bin/python
|
||||
import sys
|
||||
import os.path
|
||||
import django
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
|
||||
try:
|
||||
django.setup()
|
||||
except:
|
||||
pass
|
||||
from packages.models import Package
|
||||
import argparse
|
||||
import json
|
||||
from CLScript.CLMain import CLMain
|
||||
from loginSystem.models import Administrator
|
||||
|
||||
|
||||
class CloudLinuxPackages(CLMain):
|
||||
def __init__(self):
|
||||
CLMain.__init__(self)
|
||||
|
||||
def listAll(self, owner=None):
|
||||
packages = []
|
||||
|
||||
if owner == None:
|
||||
for items in Package.objects.all():
|
||||
try:
|
||||
packages.append({'name': items.packageName, 'owner': items.admin.userName})
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
admin = Administrator.objects.get(userName=owner)
|
||||
for items in Package.objects.filter(admin=admin):
|
||||
try:
|
||||
packages.append({'name': items.packageName, 'owner': items.admin.userName})
|
||||
except:
|
||||
pass
|
||||
|
||||
final = {'data': packages, 'metadata': self.initialMeta}
|
||||
print(json.dumps(final))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='CyberPanel CloudLinux Manager')
|
||||
parser.add_argument('-o', '--owner', help='Owner')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
pi = CloudLinuxPackages()
|
||||
try:
|
||||
pi.listAll(args.owner)
|
||||
except:
|
||||
pi.listAll()
|
||||
64
CLScript/CloudLinuxResellers.py
Normal file
64
CLScript/CloudLinuxResellers.py
Normal file
@@ -0,0 +1,64 @@
|
||||
#!/usr/local/CyberCP/bin/python
|
||||
import sys
|
||||
import os.path
|
||||
import django
|
||||
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
|
||||
try:
|
||||
django.setup()
|
||||
except:
|
||||
pass
|
||||
from loginSystem.models import Administrator, ACL
|
||||
import argparse
|
||||
import json
|
||||
from CLScript.CLMain import CLMain
|
||||
|
||||
class CloudLinuxResellers(CLMain):
|
||||
|
||||
def __init__(self, id, name):
|
||||
CLMain.__init__(self)
|
||||
self.id = id
|
||||
self.name = name
|
||||
|
||||
def listAll(self, owner=None):
|
||||
import pwd
|
||||
users = []
|
||||
acl = ACL.objects.get(name='reseller')
|
||||
from plogical.vhost import vhost
|
||||
for items in Administrator.objects.filter(acl=acl):
|
||||
if self.name != None:
|
||||
if self.name != items.userName:
|
||||
continue
|
||||
|
||||
|
||||
try:
|
||||
uid = pwd.getpwnam(items.userName).pw_uid
|
||||
except:
|
||||
vhost.addUser(items.userName, '/home/%s' % (items.userName))
|
||||
uid = pwd.getpwnam(items.userName).pw_uid
|
||||
|
||||
user = {'name': items.userName,
|
||||
"locale_code": "EN_us",
|
||||
"email": items.email,
|
||||
"id": uid
|
||||
}
|
||||
|
||||
users.append(user)
|
||||
|
||||
final = {'data': users, 'metadata': self.initialMeta}
|
||||
print(json.dumps(final))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='CyberPanel CloudLinux Manager')
|
||||
parser.add_argument('--id', help='Owner')
|
||||
parser.add_argument('-n', '--name', help='Owner')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
pi = CloudLinuxResellers(args.id, args.name)
|
||||
try:
|
||||
pi.listAll()
|
||||
except:
|
||||
pi.listAll()
|
||||
170
CLScript/CloudLinuxUsers.py
Normal file
170
CLScript/CloudLinuxUsers.py
Normal file
@@ -0,0 +1,170 @@
|
||||
#!/usr/local/CyberCP/bin/python
|
||||
import sys
|
||||
import os.path
|
||||
import django
|
||||
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
|
||||
try:
|
||||
django.setup()
|
||||
except:
|
||||
pass
|
||||
from websiteFunctions.models import Websites
|
||||
import argparse
|
||||
import pwd
|
||||
import json
|
||||
from CLScript.CLMain import CLMain
|
||||
|
||||
|
||||
class CloudLinuxUsers(CLMain):
|
||||
|
||||
def __init__(self, owner, username, packageName, packageOwner, fields, uid):
|
||||
|
||||
CLMain.__init__(self)
|
||||
self.owner = owner
|
||||
self.username = username
|
||||
self.packageName = packageName
|
||||
self.packageOwner = packageOwner
|
||||
self.fields = fields
|
||||
if uid!=None:
|
||||
self.uid = int(uid)
|
||||
else:
|
||||
self.uid = uid
|
||||
|
||||
if self.fields == None:
|
||||
self.id = 1
|
||||
self.un = 1
|
||||
self.ow = 1
|
||||
self.domain = 1
|
||||
self.package = 1
|
||||
self.email = 1
|
||||
self.localecode = 1
|
||||
else:
|
||||
if self.fields.find('id') > -1:
|
||||
self.id = 1
|
||||
else:
|
||||
self.id = 0
|
||||
if self.fields.find('username') > -1:
|
||||
self.un = 1
|
||||
else:
|
||||
self.un = 0
|
||||
|
||||
if self.fields.find('owner') > -1:
|
||||
self.ow = 1
|
||||
else:
|
||||
self.ow = 0
|
||||
|
||||
if self.fields.find('domain') > -1:
|
||||
self.domain = 1
|
||||
else:
|
||||
self.domain = 0
|
||||
|
||||
if self.fields.find('package') > -1:
|
||||
self.package = 1
|
||||
else:
|
||||
self.package = 0
|
||||
|
||||
if self.fields.find('email') > -1:
|
||||
self.email = 1
|
||||
else:
|
||||
self.email = 0
|
||||
|
||||
if self.fields.find('locale_code') > -1:
|
||||
self.localecode = 1
|
||||
else:
|
||||
self.localecode = 0
|
||||
|
||||
|
||||
|
||||
def fetchJson(self, websites):
|
||||
users = []
|
||||
for webs in websites:
|
||||
try:
|
||||
itemPackage = webs.package
|
||||
package = {'name': itemPackage.packageName, 'owner': webs.admin.userName}
|
||||
|
||||
user = {}
|
||||
|
||||
if self.id:
|
||||
user['id'] = pwd.getpwnam(webs.externalApp).pw_uid
|
||||
|
||||
if self.un:
|
||||
user['username'] = webs.externalApp
|
||||
|
||||
if self.ow:
|
||||
if webs.admin.owner == 1:
|
||||
user['owner'] = webs.admin.userName
|
||||
else:
|
||||
from loginSystem.models import Administrator
|
||||
oAdmin = Administrator.objects.get(pk=webs.admin.owner)
|
||||
user['owner'] = oAdmin.userName
|
||||
|
||||
if self.domain:
|
||||
user['domain'] = webs.domain
|
||||
|
||||
if self.package:
|
||||
user['package'] = package
|
||||
|
||||
if self.email:
|
||||
user['email'] = webs.adminEmail
|
||||
|
||||
if self.localecode:
|
||||
user['locale_code'] = "EN_us"
|
||||
|
||||
|
||||
if self.packageName != None:
|
||||
if self.package:
|
||||
if self.packageName == user['package']['name'] and self.packageOwner == user['package']['owner']:
|
||||
pass
|
||||
else:
|
||||
continue
|
||||
|
||||
if self.uid !=None:
|
||||
if self.id:
|
||||
if self.uid == user['id']:
|
||||
users.append(user)
|
||||
else:
|
||||
users.append(user)
|
||||
else:
|
||||
users.append(user)
|
||||
except BaseException as msg:
|
||||
pass
|
||||
|
||||
final = {'data': users, 'metadata': self.initialMeta}
|
||||
print(json.dumps(final))
|
||||
|
||||
def listAll(self):
|
||||
|
||||
if self.owner == None:
|
||||
websites = Websites.objects.all()
|
||||
else:
|
||||
from loginSystem.models import Administrator
|
||||
from plogical.acl import ACLManager
|
||||
oAdmin = Administrator.objects.get(userName=self.owner)
|
||||
currentACL = ACLManager.loadedACL(oAdmin.pk)
|
||||
websites = ACLManager.findWebsiteObjects(currentACL, oAdmin.pk)
|
||||
|
||||
if self.username != None:
|
||||
websites = websites.filter(externalApp=self.username)
|
||||
|
||||
self.fetchJson(websites)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='CyberPanel CloudLinux Manager')
|
||||
parser.add_argument('-o', '--owner', help='Owner')
|
||||
parser.add_argument('--fields', help='Fields to output!')
|
||||
parser.add_argument('--username', help='Fields to output!')
|
||||
parser.add_argument('--package-name', help='Fields to output!')
|
||||
parser.add_argument('--package-owner', help='Fields to output!')
|
||||
parser.add_argument('--unix-id', help='Fields to output!')
|
||||
|
||||
args = vars(parser.parse_args())
|
||||
|
||||
pi = CloudLinuxUsers(args['owner'], args['username'], args['package_name'], args['package_owner'], args['fields'], args['unix_id'])
|
||||
pi.listAll()
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
27
CLScript/UserInfo.py
Normal file
27
CLScript/UserInfo.py
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/usr/local/CyberCP/bin/python
|
||||
import getpass
|
||||
|
||||
def main():
|
||||
import pwd
|
||||
if getpass.getuser() == 'root':
|
||||
userType = "admin"
|
||||
else:
|
||||
try:
|
||||
uid = pwd.getpwnam(getpass.getuser()).pw_uid
|
||||
userType = 'reseller'
|
||||
except:
|
||||
userType = 'user'
|
||||
|
||||
data = """{
|
||||
"userName": "%s",
|
||||
"userType": "%s",
|
||||
"lang": "en",
|
||||
"assetsUri": "/usr/local/lvemanager",
|
||||
"baseUri": "/usr/local/lvemanager",
|
||||
"defaultDomain": "cyberpanel.net"
|
||||
}""" % (getpass.getuser(), userType)
|
||||
|
||||
print(data)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
0
CLScript/__init__.py
Normal file
0
CLScript/__init__.py
Normal file
40
CLScript/panel_info.py
Normal file
40
CLScript/panel_info.py
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/usr/local/CyberCP/bin/python
|
||||
import sys
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
import json
|
||||
from CLScript.CLMain import CLMain
|
||||
|
||||
|
||||
class PanelInfo(CLMain):
|
||||
def __init__(self):
|
||||
CLMain.__init__(self)
|
||||
|
||||
def emit(self):
|
||||
|
||||
initial = {
|
||||
"name": "CyberPanel",
|
||||
"version": "%s.%s" % (self.version, self.build),
|
||||
"user_login_url": "https://%s:8090/" % (self.ipAddress),
|
||||
# "supported_cl_features": {
|
||||
# "php_selector": True,
|
||||
# "ruby_selector": True,
|
||||
# "python_selector": True,
|
||||
# "nodejs_selector": True,
|
||||
# "mod_lsapi": True,
|
||||
# "mysql_governor": True,
|
||||
# "cagefs": True,
|
||||
# "reseller_limits": True,
|
||||
# "xray": True,
|
||||
# "accelerate_wp": True,
|
||||
# "autotracing": True
|
||||
# }
|
||||
}
|
||||
|
||||
final = {'data': initial, 'metadata': self.initialMeta}
|
||||
|
||||
print(json.dumps(final))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pi = PanelInfo()
|
||||
pi.emit()
|
||||
14
CONTRIBUTING.md
Normal file
14
CONTRIBUTING.md
Normal file
@@ -0,0 +1,14 @@
|
||||
Branches
|
||||
|
||||
1.Stable-> Stable branch
|
||||
2.vX.X.X-> vX.X.X Stable branch
|
||||
3.vX.X.X-dev-> v.X.X.X Dev branch
|
||||
|
||||
Development Lifecycle
|
||||
|
||||
vX.X.X-dev will be default(master) branch. All contributors must push to latest vX.X.X-dev branch. Once development
|
||||
is complete(believed to be stable) new vX.X.X Stable branch will be created from Dev branch. Then vX.X.X Stable will
|
||||
be merged into Stable branch. After that a new vX.X.X-dev branch will be created and it will be default(master)
|
||||
branch. Old dev branch will be deleted at this stage(to save space) and no development will happen on old stable or
|
||||
dev(if not deleted) branch. All development will only take place in latest dev branch. You must not create pull
|
||||
request for any other branches other than latest dev branch.
|
||||
192
CPCent7repo.json
Normal file
192
CPCent7repo.json
Normal file
@@ -0,0 +1,192 @@
|
||||
[
|
||||
{
|
||||
"Package": "MariaDB-client.x86_64",
|
||||
"Version": "10.1.44-1.el7.centos",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "MariaDB-common.x86_64",
|
||||
"Version": "10.1.44-1.el7.centos",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "MariaDB-devel.x86_64",
|
||||
"Version": "10.1.44-1.el7.centos",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "MariaDB-server.x86_64",
|
||||
"Version": "10.1.44-1.el7.centos",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "MariaDB-shared.x86_64",
|
||||
"Version": "10.1.44-1.el7.centos",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "compat-libtidy.x86_64",
|
||||
"Version": "0.99.0-37.20091203.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "dovecot.x86_64",
|
||||
"Version": "2:2.3.10-2",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "dovecot-mysql.x86_64",
|
||||
"Version": "2:2.3.10-2",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "fastlz.x86_64",
|
||||
"Version": "0.1.0-0.1.20070619svnrev12.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "galera.x86_64",
|
||||
"Version": "25.3.28-1.rhel7.el7.centos",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "htop.x86_64",
|
||||
"Version": "2.2.0-3.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "jemalloc.x86_64",
|
||||
"Version": "3.6.0-1.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "libargon2.x86_64",
|
||||
"Version": "20161029-3.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "libbsd.x86_64",
|
||||
"Version": "0.8.3-1.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "libc-client.x86_64",
|
||||
"Version": "2007f-16.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "libdb4.x86_64",
|
||||
"Version": "4.8.30-13.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "liblzf.x86_64",
|
||||
"Version": "3.6-7.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "libmcrypt.x86_64",
|
||||
"Version": "2.5.8-13.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "libopendkim.x86_64",
|
||||
"Version": "2.11.0-0.1.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "libsodium.x86_64",
|
||||
"Version": "1.0.18-1.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "libtidy.x86_64",
|
||||
"Version": "5.4.0-1.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "luajit.x86_64",
|
||||
"Version": "2.0.4-3.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "oniguruma.x86_64",
|
||||
"Version": "5.9.5-3.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "opendbx.x86_64",
|
||||
"Version": "1.4.6-6.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "opendkim.x86_64",
|
||||
"Version": "2.11.0-0.1.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "pdns.x86_64",
|
||||
"Version": "4.2.2-1pdns.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "pdns-backend-mysql.x86_64",
|
||||
"Version": "4.2.2-1pdns.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "postfix3.x86_64",
|
||||
"Version": "2:3.4.7-1.gf.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "postfix3-ldap.x86_64",
|
||||
"Version": "2:3.4.7-1.gf.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "postfix3-mysql.x86_64",
|
||||
"Version": "2:3.4.7-1.gf.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "postfix3-pcre.x86_64",
|
||||
"Version": "2:3.4.7-1.gf.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "pure-ftpd.x86_64",
|
||||
"Version": "1.0.47-3.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "redis.x86_64",
|
||||
"Version": "3.2.12-2.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "restic.x86_64",
|
||||
"Version": "0.9.6-1.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "rsync31u.x86_64",
|
||||
"Version": "3.1.3-1.ius.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "udns.x86_64",
|
||||
"Version": "0.4-3.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "udns-devel.x86_64",
|
||||
"Version": "0.4-3.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
},
|
||||
{
|
||||
"Package": "vim-minimal.x86_64",
|
||||
"Version": "2:8.0.003-1.gf.el7",
|
||||
"Repo": "@CyberPanel"
|
||||
}
|
||||
]
|
||||
126
CPScripts/CyberpanelSnapshotByCyberpanelUser.sh
Normal file
126
CPScripts/CyberpanelSnapshotByCyberpanelUser.sh
Normal file
@@ -0,0 +1,126 @@
|
||||
#!/bin/bash
|
||||
## Author: Michael Ramsey
|
||||
## Objective Find A Cyberpanel Users Domlogs Stats for last 5 days for all of their domains. v2
|
||||
## https://gitlab.com/cyberpaneltoolsnscripts/snapshotbycyberpaneluser
|
||||
## How to use.
|
||||
# ./CyberpanelSnapshotByCyberpanelUser.sh username
|
||||
#./CyberpanelSnapshotCyberpanelUser.sh exampleuserbob
|
||||
#
|
||||
##bash <(curl -s https://gitlab.com/cyberpaneltoolsnscripts/snapshotbycyberpaneluser/-/raw/master/CyberpanelSnapshotByCyberpanelUser.sh || wget -qO - https://gitlab.com/cyberpaneltoolsnscripts/snapshotbycyberpaneluser/-/raw/master/CyberpanelSnapshotByCyberpanelUser.sh) exampleuserbob;
|
||||
##
|
||||
Username=$1
|
||||
|
||||
#CURRENTDATE=$(date +"%Y-%m-%d %T") # 2019-02-09 06:47:56
|
||||
#PreviousDay1=$(date --date='1 day ago' +"%Y-%m-%d") # 2019-02-08
|
||||
#PreviousDay2=$(date --date='2 days ago' +"%Y-%m-%d") # 2019-02-07
|
||||
#PreviousDay3=$(date --date='3 days ago' +"%Y-%m-%d") # 2019-02-06
|
||||
#PreviousDay4=$(date --date='4 days ago' +"%Y-%m-%d") # 2019-02-05
|
||||
|
||||
#datetimeDom=$(date +"%d/%b/%Y") # 09/Feb/2019
|
||||
#datetimeDom1DaysAgo=$(date --date='1 day ago' +"%d/%b/%Y") # 08/Feb/2019
|
||||
#datetimeDom2DaysAgo=$(date --date='2 days ago' +"%d/%b/%Y") # 07/Feb/2019
|
||||
#datetimeDom3DaysAgo=$(date --date='3 days ago' +"%d/%b/%Y") # 06/Feb/2019
|
||||
#datetimeDom4DaysAgo=$(date --date='4 days ago' +"%d/%b/%Y") # 05/Feb/2019
|
||||
|
||||
#Domlog Date array for past 5 days
|
||||
declare -a datetimeDomLast5_array=($(date +"%d/%b/%Y") $(date --date='1 day ago' +"%d/%b/%Y") $(date --date='2 days ago' +"%d/%b/%Y") $(date --date='3 days ago' +"%d/%b/%Y") $(date --date='4 days ago' +"%d/%b/%Y")); #for DATE in "${datetimeDomLast5_array[@]}"; do echo $DATE; done;
|
||||
|
||||
#Get users homedir path
|
||||
user_homedir=$(sudo egrep "^${Username}:" /etc/passwd | cut -d: -f6)
|
||||
|
||||
#setup Domlogs/Accesslog path based off user_homedir/logs
|
||||
domlogs_path="${user_homedir}/logs/"
|
||||
|
||||
Now=$(date +"%Y-%m-%d_%T")
|
||||
|
||||
user_CyberpanelSnapshot="${Username}-CyberpanelSnapshot_${Now}.txt";
|
||||
|
||||
#create logfile in user's homedirectory.
|
||||
#sudo touch "$user_CyberpanelSnapshot"
|
||||
|
||||
#chown logfile to user
|
||||
#sudo chown ${Username}:${Username} "$user_CyberpanelSnapshot";
|
||||
|
||||
|
||||
main_function() {
|
||||
|
||||
|
||||
echo ""
|
||||
echo "Web Traffic Stats Check";
|
||||
|
||||
echo "";
|
||||
for DATE in "${datetimeDomLast5_array[@]}"; do
|
||||
echo "=============================================================";
|
||||
echo "Apache Dom Logs POST Requests for ${DATE} for $Username";
|
||||
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $1}' | cut -d: -f1|sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'| sort | uniq -c | sort -rn | head
|
||||
echo ""
|
||||
echo "HTTP Dom Logs GET Requests for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep GET | awk '{print $1}' | cut -d: -f1 |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'| sort | uniq -c | sort -rn | head
|
||||
echo ""
|
||||
echo "HTTP Dom Logs Top 10 bot/crawler requests per domain name for ${DATE}"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep -Ei 'crawl|bot|spider|yahoo|bing|google'| awk '{print $1}' | cut -d: -f1|sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'| sort | uniq -c | sort -rn | head
|
||||
echo ""
|
||||
echo "HTTP Dom Logs top ten IPs for ${DATE} for $Username"
|
||||
|
||||
command=$(sudo grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $1}'|sed -e 's/^[^=:]*[=:]//' -e 's|"||g' | sort | uniq -c | sort -rn | head| column -t);readarray -t iparray < <( echo "${command}" | tr '/' '\n'); echo ""; for IP in "${iparray[@]}"; do echo "$IP"; done; echo ""; echo "Show unique IP's with whois IP, Country,and ISP"; echo ""; for IP in "${iparray[@]}"; do IP=$(echo "$IP" |grep -Eo '([0-9]{1,3}[.]){3}[0-9]{1,3}|(*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:)))(%.+)?\s*)'); whois -h whois.cymru.com " -c -p $IP"|cut -d"|" -f 2,4,5|grep -Ev 'IP|whois.cymru.com'; done
|
||||
|
||||
echo ""
|
||||
echo "Checking the IPs that Have Hit the Server Most and What Site they were hitting:"
|
||||
sudo grep -rs "$DATE" ${domlogs_path} | awk {'print $1'} |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log:/ /g'| sort | uniq -c | sort -n | tail -10| sort -rn| column -t
|
||||
echo ""
|
||||
echo "Checking the Top Hits Per Site Per IP:"
|
||||
sudo grep -rs "$DATE" ${domlogs_path} | awk {'print $1,$6,$7'} |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log:/ /g'| sort | uniq -c | sort -n | tail -15| sort -rn| column -t
|
||||
echo ""
|
||||
echo "Apache Dom Logs find the top number of uri's being requested for ${DATE}"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $7}' | cut -d: -f2 |sed "s|$domlogs_path||g"| sort | uniq -c | sort -rn | head| column -t
|
||||
echo ""
|
||||
echo "";
|
||||
echo "View Apache requests per hour for $Username";
|
||||
sudo grep -r "$DATE" ${domlogs_path} | cut -d[ -f2 | cut -d] -f1 | awk -F: '{print $2":00"}' | sort -n | uniq -c| column -t
|
||||
echo ""
|
||||
echo "CMS Checks"
|
||||
echo ""
|
||||
echo "Wordpress Checks"
|
||||
echo "Wordpress Login Bruteforcing checks for wp-login.php for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep -E "wp-login.php|wp-admin.php" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Wordpress Cron wp-cron.php(virtual cron) checks for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep wp-cron.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Wordpress XMLRPC Attacks checks for xmlrpc.php for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep xmlrpc.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Wordpress Heartbeat API checks for admin-ajax.php for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep admin-ajax.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn;
|
||||
echo ""
|
||||
echo "CMS Bruteforce Checks"
|
||||
echo "Drupal Login Bruteforcing checks for user/login/ for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep -E "user/login/" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Magento Login Bruteforcing checks for admin pages /admin_xxxxx/admin/index/index for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep -E "admin_[a-zA-Z0-9_]*[/admin/index/index]" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Joomla Login Bruteforcing checks for admin pages /administrator/index.php for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep -E "admin_[a-zA-Z0-9_]*[/admin/index/index]" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "vBulletin Login Bruteforcing checks for admin pages admincp for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep -E "admincp" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Opencart Login Bruteforcing checks for admin pages /admin/index.php for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep -E "/admin/index.php" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Prestashop Login Bruteforcing checks for admin pages /adminxxxx for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep -E "/admin[a-zA-Z0-9_]*$" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e 's/.access_log//g'|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
|
||||
|
||||
done;
|
||||
echo "============================================================="
|
||||
|
||||
|
||||
echo "Contents have been saved to ${user_CyberpanelSnapshot}"
|
||||
}
|
||||
|
||||
# log everything, but also output to stdout
|
||||
main_function 2>&1 | tee -a "${user_CyberpanelSnapshot}"
|
||||
491
CPScripts/EasyEngine/EasyEngine-migration.sh
Normal file
491
CPScripts/EasyEngine/EasyEngine-migration.sh
Normal file
@@ -0,0 +1,491 @@
|
||||
#!/bin/bash
|
||||
|
||||
#EasyEngine to CyberPanel migration script
|
||||
|
||||
sudoer=""
|
||||
server_port="22"
|
||||
user_name="root"
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
DIR="/opt/easyengine"
|
||||
DIR_SSL="/opt/easyengine/services/nginx-proxy/certs"
|
||||
DIR_TMP="/opt/easyengine/tmp"
|
||||
SSL="0"
|
||||
owner_user=""
|
||||
owner_group=""
|
||||
|
||||
set_header() {
|
||||
if [[ -d /opt/easyengine/sites/${domains[$i]}/app/htdocs/wp-content ]] ; then
|
||||
ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key "$sudoer wget -q -O /root/header.sh https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/CPScripts/EasyEngine/header.sh ; $sudoer bash /root/header.sh ${domains[$i]}"
|
||||
fi
|
||||
}
|
||||
|
||||
fix_permission() {
|
||||
ssh_v="ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key"
|
||||
echo -e "\nget the user and group on remote CyberPanel server...."
|
||||
owner_user=$(${ssh_v} stat -c '%U' /home/${domains[$i]})
|
||||
owner_group=$(${ssh_v} stat -c '%G' /home/${domains[$i]})
|
||||
#get user and group on remote server.
|
||||
}
|
||||
|
||||
|
||||
set_ssl_cyberpanel() {
|
||||
if [[ $SSL == "1" ]] ; then
|
||||
echo -e "\nstarting certificate and private key transfer..."
|
||||
ssh_v="ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key"
|
||||
${ssh_v} "rm -f /etc/letsencrypt/live/${domains[$i]}/fullchain.pem"
|
||||
${ssh_v} "rm -f /etc/letsencrypt/live/${domains[$i]}/privkey.pem"
|
||||
#remove current self-signed cert
|
||||
|
||||
rsync --stats -av -e "ssh -o StrictHostKeyChecking=no -p $server_port -i /root/.ssh/cyberpanel_migration_key" $cert_file root@$server_ip:/etc/letsencrypt/live/${domains[$i]}/fullchain.pem
|
||||
if [[ $? == "0" ]] ; then
|
||||
echo -e "\ncert file transferred...\n"
|
||||
else
|
||||
echo -e "\ncert file trasnfer failed..."
|
||||
clean_up
|
||||
exit
|
||||
fi
|
||||
|
||||
rsync --stats -av -e "ssh -o StrictHostKeyChecking=no -p $server_port -i /root/.ssh/cyberpanel_migration_key" $key_file root@$server_ip:/etc/letsencrypt/live/${domains[$i]}/privkey.pem
|
||||
if [[ $? == "0" ]] ; then
|
||||
echo -e "\nkey file has been succesfully transferred to CyberPanel server...\n"
|
||||
else
|
||||
echo -e "\nkey file trasnfer failed..."
|
||||
clean_up
|
||||
exit
|
||||
fi
|
||||
#rsync cert and key
|
||||
|
||||
echo -e "checking LiteSpeed status on remote Cyebrpanel server..."
|
||||
${ssh_v} "/usr/local/lsws/bin/lswsctrl stop"
|
||||
${ssh_v} "pkill lsphp"
|
||||
${ssh_v} "systemctl stop lsws"
|
||||
${ssh_v} "systemctl start lsws"
|
||||
check_string=$(${ssh_v} "ps -aux | grep litespeed | grep -v grep")
|
||||
if echo $check_string | grep -q litespeed ; then
|
||||
echo -e "\nrestart LiteSpeed successful..."
|
||||
else
|
||||
echo -e "LiteSpeed start failed..."
|
||||
fi
|
||||
fi
|
||||
#restart LSWS to apply new cert
|
||||
|
||||
}
|
||||
|
||||
show_cyberpanel_site() {
|
||||
echo -e "\nchecking current websites on remote CyberPanel server..."
|
||||
ssh_v="ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key"
|
||||
$ssh_v "cyberpanel listWebsitesPretty"
|
||||
}
|
||||
|
||||
create_database() {
|
||||
echo -e "\nstarting database creation on remote CyberPanel server..."
|
||||
ssh_v="ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key"
|
||||
|
||||
check_string=$(${ssh_v} "cyberpanel createDatabase --databaseWebsite ${domains[$i]} --dbName $WPDBNAME --dbUsername $WPDBUSER --dbPassword $WPDBPASS")
|
||||
if echo $check_string | grep -q "None" ; then
|
||||
echo -e "\ndatabase successfully created..."
|
||||
else
|
||||
echo -e "\ndatabase failed to create..."
|
||||
clean_up
|
||||
exit
|
||||
fi
|
||||
|
||||
check_string=$(${ssh_v} "mysql -u $WPDBUSER -p$WPDBPASS $WPDBNAME < /home/${domains[$i]}/$database_name ; if [ $? = 0 ] ; then echo "OK" ; fi")
|
||||
if echo $check_string | grep -q "OK" ; then
|
||||
echo -e "\nstarting database import on remote CyberPanel..."
|
||||
echo -e "\ndatabase successfully imported..."
|
||||
${ssh_v} rm -f /home/${domains[$i]}/$database_name
|
||||
else
|
||||
echo -e "\ndatabase import failed..."
|
||||
${ssh_v} rm -f /home/${domains[$i]}/$database_name
|
||||
clean_up
|
||||
exit
|
||||
fi
|
||||
|
||||
# ${ssh_v} sed -i 's|global-db:3306|localhost:3306|g' /home/${domains[$i]}/public_html/wp-config.php
|
||||
|
||||
${ssh_v} "sed -i 's|global-db:3306|/var/lib/mysql/mysql.sock|g' /home/${domains[$i]}/public_html/wp-config.php"
|
||||
#set DB HOST to local unix socket for better performance.
|
||||
|
||||
}
|
||||
|
||||
clean_up() {
|
||||
#remove all the files created during operation
|
||||
echo -e "\nstarting clean up process..."
|
||||
ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key "$sudoer wget -q -O /root/key.sh https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/CPScripts/EasyEngine/key.sh ; $sudoer bash /root/key.sh disable"
|
||||
rm -f /root/.ssh/cyberpanel_migration_key
|
||||
rm -rf /opt/easyengine/tmp
|
||||
echo -e "\nclean up successful..."
|
||||
}
|
||||
|
||||
create_site_cyberpanel() {
|
||||
ssh_v="ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key"
|
||||
echo -e "\nstarting to create ${domains[$i]} on remote CyberPanel server..."
|
||||
echo -e "\nyou may see error message on acme.sh but this is normal as actual DNS is not pointed to remote server.\n\n\n"
|
||||
check_string=$(${ssh_v} "cyberpanel createWebsite --package Default --owner admin --domainName ${domains[$i]} --email admin@${domains[$i]} --php 7.4 --ssl 1")
|
||||
if echo $check_string | grep -q "None" ; then
|
||||
echo -e "\nwebsite successfully created..."
|
||||
${ssh_v} "rm -f /home/${domains[$i]}/public_html/index.html"
|
||||
${ssh_v} "cat << EOF > /home/${domains[$i]}/public_html/.htaccess
|
||||
RewriteCond %{REQUEST_URI} (wp-config|readme|license|example)\.(txt|html) [NC,OR]
|
||||
RewriteCond %{REQUEST_URI} wp-content\/uploads\/.*php [NC,OR]
|
||||
RewriteCond %{REQUEST_URI} (^\.|/\.) [NC]
|
||||
RewriteRule .* - [F,L]
|
||||
#EasyEnine converted equivalent rule.
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule . /index.php [L]
|
||||
</IfModule>
|
||||
#WordPress default rule.
|
||||
EOF"
|
||||
#rewrite rule for similar effect on easyengine configuration.
|
||||
|
||||
else
|
||||
echo -e "\nfailed to create website..."
|
||||
echo -e "\nplease check if ${domains[$i]} is already created on remote server, and delete it"
|
||||
clean_up
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
trasnfer_file() {
|
||||
ssh_v="ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key"
|
||||
if [[ -f /opt/easyengine/sites/${domains[$i]}/app/wp-config.php ]] ; then
|
||||
echo -e "\nstarting to transfer files..."
|
||||
echo -e "\ndepends on your files , this may take a while..."
|
||||
rsync --stats -av --chown=${owner_user}:${owner_group} -e "ssh -o StrictHostKeyChecking=no -p $server_port -i /root/.ssh/cyberpanel_migration_key" /opt/easyengine/sites/${domains[$i]}/app/wp-config.php root@$server_ip:/home/${domains[$i]}/public_html/wp-config.php
|
||||
if [[ $? == "0" ]] ; then
|
||||
echo -e "\nwp-config.php successfully transferred..."
|
||||
else
|
||||
echo -e "\nwp-config.php trasnfer failed..."
|
||||
clean_up
|
||||
exit
|
||||
fi
|
||||
|
||||
rsync --stats -av --chown=${owner_user}:${owner_group} -e "ssh -o StrictHostKeyChecking=no -p $server_port -i /root/.ssh/cyberpanel_migration_key" /opt/easyengine/sites/${domains[$i]}/app/htdocs/ root@$server_ip:/home/${domains[$i]}/public_html/
|
||||
if [[ $? == "0" ]] ; then
|
||||
echo -e "\nsite files succesfully transferred..."
|
||||
else
|
||||
echo -e "\nsite files trasnfer failed..."
|
||||
clean_up
|
||||
exit
|
||||
fi
|
||||
|
||||
rsync --stats -av -e "ssh -o StrictHostKeyChecking=no -p $server_port -i /root/.ssh/cyberpanel_migration_key" $OUTPUT/$database_name root@$server_ip:/home/${domains[$i]}/$database_name
|
||||
if [[ $? == "0" ]] ; then
|
||||
echo -e "\ndatabase dump successfully transferred..."
|
||||
else
|
||||
echo -e "\ndatabase dump trasnfer failed..."
|
||||
clean_up
|
||||
exit
|
||||
fi
|
||||
|
||||
else
|
||||
echo -e "\nthe script currently only works with Wordpress site..."
|
||||
clean_up
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
export_cert() {
|
||||
echo -e "\nstarting to search certificates and private keys..."
|
||||
if [[ -f $DIR_SSL/${domains[$i]}.crt ]] && [[ -f $DIR_SSL/${domains[$i]}.key ]] ; then
|
||||
echo -e "\n${domains[$i]} cert detected..."
|
||||
echo -e "\n${domains[$i]} key detected..."
|
||||
SSL="1"
|
||||
cert_file="$DIR_SSL/${domains[$i]}.crt"
|
||||
key_file="$DIR_SSL/${domains[$i]}.key"
|
||||
else
|
||||
SSL="0"
|
||||
echo -e "\n${domains[$i]} cert not found..."
|
||||
echo -e "\n${domains[$i]} key not found..."
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
fetch_cyberpanel_key() {
|
||||
if [[ ! -d /root/.ssh ]] ; then
|
||||
mkdir /root/.ssh
|
||||
chmod 700 /root/.ssh
|
||||
fi
|
||||
echo -e "\nPlease input your CyberPanel server address"
|
||||
printf "%s" "Server Address: "
|
||||
read server_ip
|
||||
if [[ $server_ip == "" ]] ; then
|
||||
echo -e "\nPlease enter a valid address"
|
||||
exit
|
||||
fi
|
||||
echo -e "\nremote server is set to $server_ip..."
|
||||
echo -e "\nPlease input your CyberPanel server SSH port"
|
||||
echo -e "Press Enter key to use port 22 as default."
|
||||
printf "%s" "SSH port: "
|
||||
read server_port
|
||||
re='^[0-9]+$'
|
||||
if [[ $server_port == "" ]] ; then
|
||||
server_port="22"
|
||||
elif [[ ! $server_port =~ $re ]] ; then
|
||||
echo -e "\nPlease input a valid port number."
|
||||
fi
|
||||
echo -e "\nSSH port is set to $server_port..."
|
||||
echo -e "\nPlease input the user name , this must be root user or sudo user."
|
||||
echo -e "Press Enter key to use root user as default."
|
||||
printf "%s" "Username: "
|
||||
read user_name
|
||||
if [[ $user_name == "" ]] ; then
|
||||
echo -e "\nset username to root..."
|
||||
user_name="root"
|
||||
sudoer=""
|
||||
elif [[ $user_name == "root" ]] ; then
|
||||
sudoer=""
|
||||
else
|
||||
sudoer="sudo -S"
|
||||
fi
|
||||
#ask user to input server IP , port and user name
|
||||
|
||||
echo -e "\nlogin username is set to $user_name"
|
||||
if grep -q "PRIVATE KEY" /root/.ssh/cyberpanel_migration_key 2>/dev/null ; then
|
||||
status=$(ssh -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key echo ok 2>&1)
|
||||
if [[ $status == ok ]] ; then
|
||||
echo -e "\nvalid key detected..."
|
||||
return
|
||||
else
|
||||
echo -e "\nunable to connect to remote server..."
|
||||
clean_up
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e "\nPlease input the password , if you are using public key authentication,please press Enter key."
|
||||
printf "%s" "Password: "
|
||||
stty -echo
|
||||
read password
|
||||
stty echo
|
||||
echo ""
|
||||
|
||||
if [[ $password == "" ]] ; then
|
||||
echo -e "\nPlease input the private key file with absolute path"
|
||||
echo -e "\ne.g. /root/.ssh/id_rsa"
|
||||
printf "%s" "key path: "
|
||||
read password
|
||||
fi
|
||||
|
||||
if [[ $password == "" ]] ; then
|
||||
echo -e "Please enter a valid path."
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ -f $password ]] ; then
|
||||
#check the input , if it's a file , consider it as key.
|
||||
ssh -o StrictHostKeyChecking=no $user_name@$server_ip -p$server_port -i $password "$sudoer wget -q -O /root/key.sh https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/CPScripts/EasyEngine/key.sh ; $sudoer bash /root/key.sh enable"
|
||||
if [[ $? == "0" ]] ; then
|
||||
ssh -o StrictHostKeyChecking=no $user_name@$server_ip -p$server_port -i $password "$sudoer cat /root/.ssh/cyberpanel_migration_key" > /root/.ssh/cyberpanel_migration_key
|
||||
if [[ $? == "0" ]] ; then
|
||||
chmod 400 /root/.ssh/cyberpanel_migration_key
|
||||
status=$(ssh -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key echo ok 2>&1)
|
||||
if [[ $status == ok ]] ; then
|
||||
echo -e "\nvalid key detected..."
|
||||
else
|
||||
echo -e "\nunabel to connect remote server..."
|
||||
clean_up
|
||||
exit
|
||||
fi
|
||||
else
|
||||
clean_up
|
||||
echo -e "\nunable to set remote key..."
|
||||
exit
|
||||
fi
|
||||
else
|
||||
echo -e "\nunable to set up the key, please manually set it up..."
|
||||
clean_up
|
||||
exit
|
||||
fi
|
||||
else
|
||||
#if it's not file , consider it as password
|
||||
sshpass -p "${password}" ssh -o StrictHostKeyChecking=no $user_name@$server_ip -p$server_port "$sudoer wget -q -O /root/key.sh https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/CPScripts/EasyEngine/key.sh ; $sudoer bash /root/key.sh enable"
|
||||
if [[ $? == "0" ]] ; then
|
||||
sshpass -p "${password}" ssh -o StrictHostKeyChecking=no $user_name@$server_ip -p$server_port "$sudoer cat /root/.ssh/cyberpanel_migration_key" > /root/.ssh/cyberpanel_migration_key
|
||||
chmod 400 /root/.ssh/cyberpanel_migration_key
|
||||
status=$(ssh -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key echo ok 2>&1)
|
||||
if [[ $status == ok ]] ; then
|
||||
echo -e "\nvalid key detected..."
|
||||
else
|
||||
echo -e "\nunabel to connect remote server..."
|
||||
clean_up
|
||||
exit
|
||||
fi
|
||||
else
|
||||
echo -e "\nunable to set up the key, please manually set it up..."
|
||||
clean_up
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
install_lscwp() {
|
||||
ssh_v="ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key"
|
||||
|
||||
$ssh_v "ls -l /usr/bin/wp"
|
||||
if [[ $? != "0" ]] ; then
|
||||
$ssh_v "$sudoer wget -O /usr/bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar"
|
||||
$ssh_v "$sudoer chmod +x /usr/bin/wp"
|
||||
fi
|
||||
#install WP CLI if not yet installed.
|
||||
$ssh_v "sudo -u $owner_user -i -- wp --path=/home/${domains[$i]}/public_html plugin install litespeed-cache"
|
||||
echo -e "\nInstalling LiteSpeed Cache for WordPress..."
|
||||
|
||||
}
|
||||
|
||||
export_database() {
|
||||
WPDBNAME=`cat /opt/easyengine/sites/${domains[$i]}/app/wp-config.php | grep DB_NAME | cut -d \' -f 4`
|
||||
WPDBUSER=`cat /opt/easyengine/sites/${domains[$i]}/app/wp-config.php | grep DB_USER | cut -d \' -f 4`
|
||||
WPDBPASS=`cat /opt/easyengine/sites/${domains[$i]}/app/wp-config.php | grep DB_PASSWORD | cut -d \' -f 4`
|
||||
#get database name , user and password for mysqldump
|
||||
|
||||
echo -e "\nstarting to export database..."
|
||||
USER="root"
|
||||
PASSWORD=`cat /opt/easyengine/services/docker-compose.yml | grep MYSQL_ROOT_PASSWORD | awk -F'=' '{print $2}'`
|
||||
OUTPUT="$DIR_TMP/database"
|
||||
DOCKERDatabaseID=`docker ps | grep -e 'services_global-db' | cut -c1-12;`
|
||||
|
||||
databases=`docker exec $DOCKERDatabaseID bash -c "mysql -h localhost --user=$USER --password=$PASSWORD -e 'show databases;'" | tr -d "| " | grep -v Database`
|
||||
|
||||
for db in $databases; do
|
||||
if [[ $db == "$WPDBNAME" ]] ; then
|
||||
echo -e "\ndumping database for ${domains[$i]}..."
|
||||
sudo docker exec $DOCKERDatabaseID bash -c "/usr/bin/mysqldump -u $USER -p$PASSWORD --databases $db" > $OUTPUT/$db.sql
|
||||
database_name="$db.sql"
|
||||
if [[ $? == "0" ]] ; then
|
||||
echo -e "\ndatabase successfully exported..."
|
||||
else
|
||||
echo -e "\nfailed to export database..."
|
||||
clean_up
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
done
|
||||
#credit to https://community.easyengine.io/t/cant-create-mysqldump/12306
|
||||
|
||||
}
|
||||
|
||||
|
||||
check_dir () {
|
||||
if [[ ! -d /opt/easyengine/sites ]] ; then
|
||||
echo -e "\ncan not detect sites directory..."
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ -d $DIR_TMP ]] ; then
|
||||
rm -rf $DIR_TMP
|
||||
fi
|
||||
mkdir $DIR_TMP
|
||||
mkdir $DIR_TMP/database
|
||||
}
|
||||
|
||||
show_help() {
|
||||
echo -e "\nEasyEngine to CyberPanel Migration Script"
|
||||
echo -e "\nThis script will do:"
|
||||
echo -e "\n1. Generate public key and private key for root user on remote CyberPanel server."
|
||||
echo -e "2. Find the Wordpress sites hosting on this EasyEngine server"
|
||||
echo -e "3. Export the site's database and its SSL cert/key if available and trasnfer to remote CyberPanel server."
|
||||
echo -e "4. Create website with same domain on remote CyberPanel server and its related database."
|
||||
echo -e "5. Import database dump and set up SSL cert/key if available"
|
||||
echo -e "6. Download LiteSpeed Cache plugin for Wordpress, but it will not be enabled until you activate it."
|
||||
echo -e "7. Install PHP extension sodium imagick redis and memcached."
|
||||
echo -e "8. Once the migration process is completed, previously generated key will be removed on remote CyberPanel server."
|
||||
echo -e "9. All the temporary generated files on this server will also be cleaned up."
|
||||
echo -e "\nOnce migration is completed, you can use local host file to override the DNS record to test site on remote CyberPanel server"
|
||||
echo -e "without effecting your live site"
|
||||
echo -e "\nNo file on this server will be touched.\n"
|
||||
read -rsn1 -p "Please press any key to continue..."
|
||||
}
|
||||
|
||||
db_length_check() {
|
||||
ssh_v="ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key"
|
||||
output=$($ssh_v "$sudoer cat /usr/local/CyberCP/plogical/mysqlUtilities.py")
|
||||
if echo $output | grep -q "should be 16 at max" ; then
|
||||
echo -e "\nPlease upgrade your CyberPanel to latest first..."
|
||||
clean_up
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
check_dir
|
||||
#check if this is an easyengine server and create a temp dir for storing files during the process.
|
||||
|
||||
show_help
|
||||
|
||||
declare -a domains
|
||||
|
||||
for i in $(ls /opt/easyengine/sites);
|
||||
do
|
||||
domains=("${domains[@]}" "$i")
|
||||
done
|
||||
|
||||
echo -e "\n\nsearching websites..."
|
||||
echo -e "\ntotal number of domains: ${#domains[@]}"
|
||||
echo -e "\ndomain list: ${domains[@]}"
|
||||
|
||||
dpkg -l sshpass > /dev/null
|
||||
echo -e "\n\nchecking necessary package..."
|
||||
if [[ $? == "0" ]] ; then
|
||||
echo -e "\nsshpass package already installed...\n"
|
||||
else
|
||||
apt update
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y sshpass
|
||||
if [[ $? == "0" ]] ; then
|
||||
echo -e "\nsshpass successfully installed...\n"
|
||||
else
|
||||
echo -e "\nunable to install sshpass...\n"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
|
||||
fetch_cyberpanel_key
|
||||
#function to get cyberpanel server key so future SSH command won't require password input.
|
||||
|
||||
db_length_check
|
||||
|
||||
tLen=${#domains[@]}
|
||||
#get the domain list and number of domains.
|
||||
|
||||
for (( i=0; i<${tLen}; i++ ));
|
||||
do
|
||||
# ${domains[$i]} , domain name variable
|
||||
#create a file to save variable to source in cyberpanel server to read it.
|
||||
|
||||
export_database
|
||||
#dump all sites' database
|
||||
|
||||
export_cert
|
||||
#find the cert for this domain
|
||||
|
||||
create_site_cyberpanel
|
||||
|
||||
fix_permission
|
||||
|
||||
trasnfer_file
|
||||
|
||||
create_database
|
||||
|
||||
set_header
|
||||
|
||||
install_lscwp
|
||||
|
||||
set_ssl_cyberpanel
|
||||
done
|
||||
#for loop to run each function for each domain.
|
||||
|
||||
ssh -o StrictHostKeyChecking=no root@$server_ip -p$server_port -i /root/.ssh/cyberpanel_migration_key "$sudoer wget -q -O /root/ext.sh https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/CPScripts/EasyEngine/ext.sh ; $sudoer bash /root/ext.sh"
|
||||
#install some php ext
|
||||
|
||||
show_cyberpanel_site
|
||||
|
||||
clean_up
|
||||
#remove all the files in tmp dir after script is done.
|
||||
exit
|
||||
97
CPScripts/EasyEngine/ext.sh
Normal file
97
CPScripts/EasyEngine/ext.sh
Normal file
@@ -0,0 +1,97 @@
|
||||
#!/bin/bash
|
||||
|
||||
#script to install some lsphp74 extension
|
||||
|
||||
SERVER_OS=""
|
||||
|
||||
|
||||
hash yum 2>/dev/null
|
||||
if [[ $? == "0" ]] ; then
|
||||
echo -e "\nyum detected..."
|
||||
SERVER_OS="CentOS"
|
||||
fi
|
||||
|
||||
hash apt 2>/dev/null
|
||||
if [[ $? == "0" ]] ; then
|
||||
echo -e "\napt detected..."
|
||||
SERVER_OS="Ubuntu"
|
||||
fi
|
||||
|
||||
if [[ $SERVER_OS == "" ]] ; then
|
||||
echo -e "\nunable to detect the system..."
|
||||
exit
|
||||
fi
|
||||
|
||||
|
||||
if [[ ! -f /usr/local/lsws/lsphp74/lib64/php/modules/zip.so ]] && [[ $SERVER_OS == "CentOS" ]] ; then
|
||||
yum list installed libzip-devel
|
||||
if [[ $? == "0" ]] ; then
|
||||
yum remove -y libzip-devel
|
||||
fi
|
||||
|
||||
yum install -y http://packages.psychotic.ninja/7/plus/x86_64/RPMS/libzip-0.11.2-6.el7.psychotic.x86_64.rpm
|
||||
yum install -y http://packages.psychotic.ninja/7/plus/x86_64/RPMS/libzip-devel-0.11.2-6.el7.psychotic.x86_64.rpm
|
||||
yum install -y lsphp74-devel
|
||||
|
||||
if [[ ! -d /usr/local/lsws/lsphp74/tmp ]] ; then
|
||||
mkdir /usr/local/lsws/lsphp74/tmp
|
||||
fi
|
||||
|
||||
/usr/local/lsws/lsphp74/bin/pecl channel-update pecl.php.net
|
||||
/usr/local/lsws/lsphp74/bin/pear config-set temp_dir /usr/local/lsws/lsphp74/tmp
|
||||
/usr/local/lsws/lsphp74/bin/pecl install zip
|
||||
if [[ $? == 0 ]] ; then
|
||||
echo "extension=zip.so" > /usr/local/lsws/lsphp74/etc/php.d/20-zip.ini
|
||||
chmod 755 /usr/local/lsws/lsphp74/lib64/php/modules/zip.so
|
||||
echo -e "\nInstalling lsphp74-zip"
|
||||
else
|
||||
echo -e "\nlsphp74-zip compilation failed..."
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [[ $SERVER_OS == "CentOS" ]] ; then
|
||||
yum install -y lsphp74-redis
|
||||
echo -e "\nInstalling lsphp74-redis"
|
||||
else
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y lsphp74-redis
|
||||
echo -e "\nInstalling lsphp74-redis"
|
||||
fi
|
||||
|
||||
if [[ $SERVER_OS == "CentOS" ]] ; then
|
||||
yum install -y lsphp74-memcached
|
||||
echo -e "\nInstalling lsphp74-memcached"
|
||||
else
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y lsphp74-memcached
|
||||
echo -e "\nInstalling lsphp74-memcached"
|
||||
fi
|
||||
|
||||
if [[ $SERVER_OS == "CentOS" ]] ; then
|
||||
yum install -y lsphp74-imagick
|
||||
echo -e "\nInstalling lsphp74-imagick"
|
||||
else
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y lsphp74-imagick
|
||||
echo -e "\nInstalling lsphp74-imagick"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
if [[ $SERVER_OS == "CentOS" ]] ; then
|
||||
yum install -y lsphp74-sodium
|
||||
echo -e "\nInstalling lsphp74-sodium"
|
||||
else
|
||||
mkdir /usr/local/lsws/cyberpanel-tmp
|
||||
cd /usr/local/lsws/cyberpanel-tmp
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y libsodium-dev
|
||||
wget -O libsodium.tgz http://pecl.php.net/get/libsodium
|
||||
tar xzvf libsodium.tgz
|
||||
cd libsodium-*
|
||||
/usr/local/lsws/lsphp74/bin/phpize
|
||||
./configure --with-php-config=/usr/local/lsws/lsphp74/bin/php-config7.4
|
||||
make
|
||||
make install
|
||||
echo "extension=sodium.so" > /usr/local/lsws/lsphp74/etc/php/7.4/mods-available/20-sodium.ini
|
||||
pkill lsphp74
|
||||
echo -e "\nInstalling lsphp74-sodium"
|
||||
fi
|
||||
89
CPScripts/EasyEngine/header.sh
Normal file
89
CPScripts/EasyEngine/header.sh
Normal file
@@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
|
||||
set_header() {
|
||||
if [[ -f /usr/local/lsws/conf/vhosts/$1/vhost.conf ]] ; then
|
||||
cat << EOF > header.txt
|
||||
|
||||
context /wp-content/cache/css/ {
|
||||
location $DOC_ROOT/wp-content/cache/css/
|
||||
allowBrowse 1
|
||||
enableExpires 1
|
||||
expiresByType text/css=A15552000
|
||||
extraHeaders <<<END_extraHeaders
|
||||
unset Cache-control
|
||||
set Cache-control public, max-age=15552000
|
||||
set Access-Control-Allow-Origin: *
|
||||
END_extraHeaders
|
||||
|
||||
|
||||
rewrite {
|
||||
|
||||
}
|
||||
addDefaultCharset off
|
||||
|
||||
phpIniOverride {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
context /wp-content/cache/js/ {
|
||||
location $DOC_ROOT/wp-content/cache/js/
|
||||
allowBrowse 1
|
||||
enableExpires 1
|
||||
expiresByType application/x-javascript=A15552000, text/javascript=A15552000, application/javascript=A15552000
|
||||
extraHeaders <<<END_extraHeaders
|
||||
unset Cache-control
|
||||
set Cache-control public, max-age=15552000
|
||||
set Access-Control-Allow-Origin: *
|
||||
END_extraHeaders
|
||||
|
||||
|
||||
rewrite {
|
||||
|
||||
}
|
||||
addDefaultCharset off
|
||||
|
||||
phpIniOverride {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
context exp:^.*(css|gif|ico|jpeg|jpg|js|png|webp|woff|woff2|fon|fot|ttf)$ {
|
||||
location $DOC_ROOT/$0
|
||||
allowBrowse 1
|
||||
enableExpires 1
|
||||
expiresByType text/css=A15552000, image/gif=A15552000, image/x-icon=A15552000, image/jpeg=A15552000, application/x-javascript=A15552000, text/javascript=A15552000, application/javascript=A15552000, image/png=A15552000, image/webp=A15552000, font/ttf=A15552000, font/woff=A15552000, font/woff2=A15552000, application/x-font-ttf=A15552000, application/x-font-woff=A15552000, application/font-woff=A15552000, application/font-woff2=A15552000
|
||||
extraHeaders <<<END_extraHeaders
|
||||
unset Cache-control
|
||||
set Cache-control public, max-age=15552000
|
||||
set Access-Control-Allow-Origin: *
|
||||
END_extraHeaders
|
||||
|
||||
|
||||
rewrite {
|
||||
|
||||
}
|
||||
addDefaultCharset off
|
||||
|
||||
phpIniOverride {
|
||||
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
cat header.txt >> /usr/local/lsws/conf/vhosts/$1/vhost.conf
|
||||
fi
|
||||
}
|
||||
|
||||
if /usr/local/lsws/bin/lshttpd -v | grep -iF open ; then
|
||||
echo -e "\nOpenLiteSpeed detected..."
|
||||
set_header
|
||||
else
|
||||
echo -e "\nLiteSpeed Enterprise detected..."
|
||||
exit
|
||||
#LiteSpeed Enterprise can read htaccess for expire header, no need to set it up.
|
||||
fi
|
||||
|
||||
rm -f header.txt
|
||||
rm -f $0
|
||||
echo -e "\nexpire , cache-control and CORS header set..."
|
||||
78
CPScripts/EasyEngine/key.sh
Normal file
78
CPScripts/EasyEngine/key.sh
Normal file
@@ -0,0 +1,78 @@
|
||||
#!/bin/bash
|
||||
|
||||
# script to set up access key for non-interactive SSH login
|
||||
|
||||
check_root() {
|
||||
if [[ $(id -u) != 0 ]] > /dev/null; then
|
||||
echo -e "\nYou must use root permission...\n"
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
key_generation() {
|
||||
rm -f /root/.ssh/cyberpanel_migration_key
|
||||
rm -f /root/.ssh/cyberpanel_migration_key.pub
|
||||
ssh-keygen -t rsa -N "" -f /root/.ssh/cyberpanel_migration_key
|
||||
if [[ -f /root/.ssh/authorized_keys ]] ; then
|
||||
cp /root/.ssh/authorized_keys /root/.ssh/authorized_keys_migration
|
||||
string=$(head -c 3 /root/.ssh/authorized_keys)
|
||||
if [[ $string != "ssh" ]] ; then
|
||||
#check if it's like AWS that prohibits direct root login.
|
||||
rm -f /root/.ssh/authorized_keys
|
||||
cat /root/.ssh/cyberpanel_migration_key.pub > /root/.ssh/authorized_keys
|
||||
else
|
||||
cat /root/.ssh/cyberpanel_migration_key.pub >> /root/.ssh/authorized_keys
|
||||
fi
|
||||
else
|
||||
cat /root/.ssh/cyberpanel_migration_key.pub > /root/.ssh/authorized_keys
|
||||
chmod 600 /root/.ssh/authorized_keys
|
||||
fi
|
||||
|
||||
echo -e "\nsuccessfully set up public key and private key for migration..."
|
||||
# this function creates public key and private key
|
||||
}
|
||||
|
||||
ssh_config() {
|
||||
rm -f /etc/ssh/sshd_config_migration
|
||||
cp /etc/ssh/sshd_config /etc/ssh/sshd_config_migration
|
||||
if grep -q "#PubkeyAuthentication yes" /etc/ssh/sshd_config ; then
|
||||
sed -i 's|#PubkeyAuthentication yes|PubkeyAuthentication yes|g' /etc/ssh/sshd_config
|
||||
fi
|
||||
systemctl restart sshd
|
||||
#this function will modify ssh configuration to allow public key login and root login
|
||||
}
|
||||
|
||||
|
||||
revert_change() {
|
||||
if [[ ! -f /etc/ssh/sshd_config_migration ]] ; then
|
||||
echo -e "You didn't enable it..."
|
||||
exit
|
||||
else
|
||||
rm -f /root/.ssh/authorized_keys
|
||||
rm -f /etc/ssh/sshd_config
|
||||
rm -f /root/.ssh/cyberpanel_migration_key
|
||||
rm -f /root/.ssh/cyberpanel_migration_key.pub
|
||||
cp /etc/ssh/sshd_config_migration /etc/ssh/sshd_config
|
||||
if [[ -f /root/.ssh/authorized_keys_migration ]] ; then
|
||||
cp /root/.ssh/authorized_keys_migration /root/.ssh/authorized_keys
|
||||
rm -f /root/.ssh/authorized_keys_migration
|
||||
fi
|
||||
systemctl restart sshd
|
||||
fi
|
||||
echo -e "\nsuccessfully removed public key and private key for migration..."
|
||||
|
||||
#this function will revert the changes and restore backed up files.
|
||||
}
|
||||
|
||||
check_root
|
||||
|
||||
|
||||
if [[ $1 == "enable" ]] ; then
|
||||
ssh_config
|
||||
key_generation
|
||||
elif [[ $1 == "disable" ]] ; then
|
||||
revert_change
|
||||
else
|
||||
echo -e "\nPlease use argument enable or disable"
|
||||
echo -e "\ne.g. ./key.sh enable\n"
|
||||
fi
|
||||
144
CPScripts/access-logparser-user.sh
Normal file
144
CPScripts/access-logparser-user.sh
Normal file
@@ -0,0 +1,144 @@
|
||||
#!/bin/bash
|
||||
## Author: Michael Ramsey
|
||||
## Objective Find A Cyberpanel/cPanel Users Dom/Access logs Stats for last 5 days for all of their domains from inside the account. Great for end users without Sudo/Root access in a shared setup to check their own logs.
|
||||
## https://gitlab.com/mikeramsey/access-log-parser
|
||||
## How to use.
|
||||
# Run the script from your account via manual or curl method.
|
||||
#
|
||||
# ./access-logparser-user.sh
|
||||
#
|
||||
#
|
||||
##bash <(curl -s https://gitlab.com/mikeramsey/access-log-parser/-/raw/master/access-logparser-user.sh || wget -qO - https://gitlab.com/mikeramsey/access-log-parser/-/raw/master/access-logparser-user.sh) ;
|
||||
##
|
||||
Username=${USER}
|
||||
|
||||
#Detect Control panel
|
||||
if [ -f /usr/local/cpanel/cpanel ]; then
|
||||
# Cpanel check for /usr/local/cpanel/cpanel -V
|
||||
ControlPanel="cpanel"
|
||||
datetimeDcpumon=$(date +"%Y/%b/%d") # 2019/Feb/15
|
||||
#Current Dcpumon file
|
||||
# DcpumonCurrentLOG="/var/log/dcpumon/${datetimeDcpumon}" # /var/log/dcpumon/2019/Feb/15
|
||||
#Setup datetimeDcpumonLast5_array
|
||||
# declare -a datetimeDcpumonLast5_array=($(date +"%Y/%b/%d") $(date --date='1 day ago' +"%Y/%b/%d") $(date --date='2 days ago' +"%Y/%b/%d") $(date --date='3 days ago' +"%Y/%b/%d") $(date --date='4 days ago' +"%Y/%b/%d")); #for DATE in "${datetimeDcpumonLast5_array[@]}"; do echo $DATE; done;
|
||||
|
||||
user_homedir=${HOME}
|
||||
user_accesslogs="${HOME}/logs/"
|
||||
domlogs_path="/usr/local/apache/domlogs/${Username}/"
|
||||
acesslog_sed="-ssl_log"
|
||||
|
||||
elif [ -f /usr/bin/cyberpanel ]; then
|
||||
# CyberPanel check /usr/bin/cyberpanel
|
||||
ControlPanel="cyberpanel"
|
||||
|
||||
#Get users homedir path
|
||||
user_homedir=${HOME}
|
||||
domlogs_path="${user_homedir}/logs/"
|
||||
acesslog_sed=".access_log"
|
||||
|
||||
else
|
||||
echo "Not able to detect Control panel. Unsupported Control Panel exiting now"
|
||||
exit 1;
|
||||
fi
|
||||
echo "=============================================================";
|
||||
echo "$ControlPanel Control Panel Detected"
|
||||
echo "User Homedirectory: ${user_homedir}"
|
||||
echo "User Domlogs Path: ${domlogs_path}"
|
||||
echo "=============================================================";
|
||||
echo "";
|
||||
#Domlog Date array for past 5 days
|
||||
declare -a datetimeDomLast5_array=($(date +"%d/%b/%Y") $(date --date='1 day ago' +"%d/%b/%Y") $(date --date='2 days ago' +"%d/%b/%Y") $(date --date='3 days ago' +"%d/%b/%Y") $(date --date='4 days ago' +"%d/%b/%Y")); #for DATE in "${datetimeDomLast5_array[@]}"; do echo $DATE; done;
|
||||
|
||||
|
||||
Now=$(date +"%Y-%m-%d_%T")
|
||||
|
||||
user_Snapshot="${Username}-Snapshot_${Now}.txt";
|
||||
|
||||
#create logfile in user's homedirectory.
|
||||
touch ${user_Snapshot}
|
||||
|
||||
#chown logfile to user
|
||||
#sudo chown ${Username}:${Username} "$user_CyberpanelSnapshot";
|
||||
|
||||
|
||||
main_function() {
|
||||
|
||||
echo ""
|
||||
echo "Web Traffic Stats Check";
|
||||
|
||||
echo "";
|
||||
for DATE in "${datetimeDomLast5_array[@]}"; do
|
||||
echo "=============================================================";
|
||||
echo "HTTP Dom Logs POST Requests for ${DATE} for $Username";
|
||||
|
||||
grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $1}' | cut -d: -f1|sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"| sort | uniq -c | sort -rn | head
|
||||
echo ""
|
||||
echo "HTTP Dom Logs GET Requests for ${DATE} for $Username"
|
||||
grep -r "$DATE" ${domlogs_path} | grep GET | awk '{print $1}' | cut -d: -f1 |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"| sort | uniq -c | sort -rn | head
|
||||
echo ""
|
||||
echo "HTTP Dom Logs Top 10 bot/crawler requests per domain name for ${DATE}"
|
||||
grep -r "$DATE" ${domlogs_path} | grep -Ei 'crawl|bot|spider|yahoo|bing|google'| awk '{print $1}' | cut -d: -f1|sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"| sort | uniq -c | sort -rn | head
|
||||
echo ""
|
||||
echo "HTTP Dom Logs top ten IPs for ${DATE} for $Username"
|
||||
|
||||
command=$(grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $1}'|sed -e 's/^[^=:]*[=:]//' -e 's|"||g' | sort | uniq -c | sort -rn | head| column -t);readarray -t iparray < <( echo "${command}" | tr '/' '\n'); echo ""; for IP in "${iparray[@]}"; do echo "$IP"; done; echo ""; echo "Show unique IP's with whois IP, Country,and ISP"; echo ""; for IP in "${iparray[@]}"; do IP=$(echo "$IP" |grep -Eo '([0-9]{1,3}[.]){3}[0-9]{1,3}|(*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:)))(%.+)?\s*)'); whois -h whois.cymru.com " -c -p $IP"|cut -d"|" -f 2,4,5|grep -Ev 'IP|whois.cymru.com'; done
|
||||
|
||||
echo ""
|
||||
echo "Checking the IPs that Have Hit the Server Most and What Site they were hitting:"
|
||||
grep -rs "$DATE" ${domlogs_path} | awk {'print $1'} |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed:| |g"| sort | uniq -c | sort -n | tail -10| sort -rn| column -t
|
||||
echo ""
|
||||
echo "Checking the Top Hits Per Site Per IP:"
|
||||
grep -rs "$DATE" ${domlogs_path} | awk {'print $1,$6,$7'} |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed:| |g"| sort | uniq -c | sort -n | tail -10| sort -rn| column -t
|
||||
echo ""
|
||||
echo "HTTP Dom Logs find the top number of uri's being requested for ${DATE}"
|
||||
grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $7}' | cut -d: -f2 |sed "s|$domlogs_path||g"| sort | uniq -c | sort -rn | head| column -t
|
||||
echo ""
|
||||
echo "";
|
||||
echo "View HTTP requests per hour for $Username";
|
||||
grep -r "$DATE" ${domlogs_path} | cut -d[ -f2 | cut -d] -f1 | awk -F: '{print $2":00"}' | sort -n | uniq -c| column -t
|
||||
echo ""
|
||||
echo "CMS Checks"
|
||||
echo ""
|
||||
echo "Wordpress Checks"
|
||||
echo "Wordpress Login Bruteforcing checks for wp-login.php for ${DATE} for $Username"
|
||||
grep -r "$DATE" ${domlogs_path} | grep wp-login.php | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Wordpress Cron wp-cron.php(virtual cron) checks for ${DATE} for $Username"
|
||||
grep -r "$DATE" ${domlogs_path} | grep wp-cron.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Wordpress XMLRPC Attacks checks for xmlrpc.php for ${DATE} for $Username"
|
||||
grep -r "$DATE" ${domlogs_path} | grep xmlrpc.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Wordpress Heartbeat API checks for admin-ajax.php for ${DATE} for $Username"
|
||||
grep -r "$DATE" ${domlogs_path} | grep admin-ajax.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn;
|
||||
echo ""
|
||||
echo "CMS Bruteforce Checks"
|
||||
echo "Drupal Login Bruteforcing checks for user/login/ for ${DATE} for $Username"
|
||||
grep -r "$DATE" ${domlogs_path} | grep -E "user/login/" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Magento Login Bruteforcing checks for admin pages /admin_xxxxx/admin/index/index for ${DATE} for $Username"
|
||||
grep -r "$DATE" ${domlogs_path} | grep -E "admin_[a-zA-Z0-9_]*[/admin/index/index]" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Joomla Login Bruteforcing checks for admin pages /administrator/index.php for ${DATE} for $Username"
|
||||
grep -r "$DATE" ${domlogs_path} | grep -E "/administrator/index.php" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "vBulletin Login Bruteforcing checks for admin pages admincp for ${DATE} for $Username"
|
||||
grep -r "$DATE" ${domlogs_path} | grep -E "admincp" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Opencart Login Bruteforcing checks for admin pages /admin/index.php for ${DATE} for $Username"
|
||||
grep -r "$DATE" ${domlogs_path} | grep -E "/admin/index.php" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Prestashop Login Bruteforcing checks for admin pages /adminxxxx for ${DATE} for $Username"
|
||||
grep -r "$DATE" ${domlogs_path} | grep -E "/admin[a-zA-Z0-9_]*$" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
|
||||
|
||||
done;
|
||||
echo "============================================================="
|
||||
|
||||
|
||||
echo "Contents have been saved to ${user_Snapshot}"
|
||||
}
|
||||
|
||||
# log everything, but also output to stdout
|
||||
main_function 2>&1 | tee -a "${user_Snapshot}"
|
||||
601
CPScripts/access-logparser.py
Normal file
601
CPScripts/access-logparser.py
Normal file
@@ -0,0 +1,601 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Apache Regex portion original credits to: https://leancrew.com/all-this/2013/07/parsing-my-apache-logs/
|
||||
## https://gitlab.com/mikeramsey/access-log-parser
|
||||
## How to use.
|
||||
# Run the script from your account via manual or curl method. It autodetects the current user and defaults to the todays date if not argument for how many days ago it provided.
|
||||
# For todays hits
|
||||
# ./access-logparser.py
|
||||
#
|
||||
# For yesterdays aka 1 Days ago
|
||||
# ./access-logparser.py 1
|
||||
#
|
||||
##python <(curl -s https://gitlab.com/mikeramsey/access-log-parser/-/raw/master/access-logparser.py || wget -qO - https://gitlab.com/mikeramsey/access-log-parser/-/raw/master/access-logparser.py) 1;
|
||||
|
||||
|
||||
__author__ = "Michael Ramsey"
|
||||
__version__ = "0.1.2"
|
||||
__license__ = "GPL-3.0"
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
from collections import Counter
|
||||
from datetime import date, timedelta
|
||||
from datetime import datetime
|
||||
from os.path import join, isfile
|
||||
import getpass
|
||||
import glob
|
||||
|
||||
|
||||
# import pathlib
|
||||
|
||||
# print('version is', sys.version)
|
||||
|
||||
|
||||
def main():
|
||||
script = sys.argv[0]
|
||||
# filename = sys.argv[2]
|
||||
# filenametest = "/home/example.com.access_log"
|
||||
# username = 'server'
|
||||
username = getpass.getuser()
|
||||
# print(username)
|
||||
# Define the day of interest in the Apache common log format. Default if not specified
|
||||
try:
|
||||
daysago = int(sys.argv[1])
|
||||
# daysago = 0
|
||||
except:
|
||||
daysago = 0
|
||||
the_day = date.today() - timedelta(daysago)
|
||||
apache_day = the_day.strftime('[%d/%b/%Y:')
|
||||
dcpumon_day = the_day.strftime('%Y/%b/%d')
|
||||
|
||||
# Set variables to empty
|
||||
controlpanel = ''
|
||||
domlogs_path = ''
|
||||
|
||||
try:
|
||||
if os.path.isfile('/usr/local/cpanel/cpanel') | os.path.isfile(os.getcwd() + '/cpanel'):
|
||||
controlpanel = 'Cpanel'
|
||||
datetime_dcpumon = date.today().strftime('%Y/%b/%d') # 2020/Feb/10
|
||||
# Current Dcpumon file
|
||||
dcpumon_current_log = "/var/log/dcpumon/" + datetime_dcpumon # /var/log/dcpumon/2019/Feb/15
|
||||
acesslog_sed = "-ssl_log"
|
||||
if username == 'root':
|
||||
domlogs_path = '/usr/local/apache/domlogs/'
|
||||
else:
|
||||
user_homedir = "/home/" + username
|
||||
user_accesslogs = "/home/" + username + "/logs/"
|
||||
domlogs_path = "/usr/local/apache/domlogs/" + username
|
||||
|
||||
elif os.path.isfile('/usr/bin/cyberpanel') | os.path.isfile(os.getcwd() + '/cyberpanel'):
|
||||
controlpanel = 'CyberPanel'
|
||||
acesslog_sed = ".access_log"
|
||||
if username == 'root':
|
||||
# Needs updated to glob all /home/*/logs/
|
||||
domlogs_path2 = glob.glob('/home/*/logs/')
|
||||
else:
|
||||
# Get users homedir path
|
||||
user_homedir = os.path.expanduser("~" + username)
|
||||
domlogs_path = user_homedir + "/logs/"
|
||||
|
||||
except:
|
||||
controlpanel = 'Control Panel not found'
|
||||
|
||||
# Define Output file
|
||||
stats_output = open(os.getcwd() + '/stats.txt', "w")
|
||||
|
||||
if username == 'root' and controlpanel == 'CyberPanel':
|
||||
# Needs updated to glob all /home/*/logs/
|
||||
path = '/home/*/logs/*'
|
||||
domlogs_path = glob.glob("/home/*/logs/")
|
||||
print('Root CyberPanel Detected')
|
||||
# Get list of dir contents
|
||||
# logs_path_contents = glob.glob("/home/*/logs/*.access_log", recursive=True)
|
||||
|
||||
# Get list of files only from this directory
|
||||
logs = glob.glob("/home/*/logs/*.access_log")
|
||||
|
||||
else:
|
||||
# Define log path directory
|
||||
path = domlogs_path
|
||||
# Get list of dir contents
|
||||
logs_path_contents = os.listdir(path)
|
||||
# Get list of files only from this directory
|
||||
logs = filter(lambda f: isfile(join(path, f)), logs_path_contents)
|
||||
|
||||
# Regex for the Apache common log format.
|
||||
parts = [ # host %h :ip/hostname of the client 172.68.142.138
|
||||
# indent %l (unused) :client identity via client's identd configuration -
|
||||
# user %u :HTTP authenticated user ID -
|
||||
# time %t :timestamp [09/Mar/2019:00:38:03 -0600]
|
||||
# request "%r" :request method of request, resource requested, & protocol "POST /wp-login.php HTTP/1.1"
|
||||
# status %>s :Apache status code 404
|
||||
# size %b (careful,can be'-'):size of request in bytes, excluding headers 3767
|
||||
# referrer "%{Referer}i" :Referer "https://www.google.com/"
|
||||
# user agent "%{User-agent}i":User-Agent "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0"
|
||||
r'(?P<host>\S+)',
|
||||
r'\S+',
|
||||
r'(?P<user>\S+)',
|
||||
r'\[(?P<time>.+)\]',
|
||||
r'"(?P<request>.*)"',
|
||||
r'(?P<status>[0-9]+)',
|
||||
r'(?P<size>\S+)',
|
||||
r'"(?P<referrer>.*)"',
|
||||
r'"(?P<agent>.*)"',
|
||||
]
|
||||
pattern = re.compile(r'\s+'.join(parts) + r'\s*\Z')
|
||||
|
||||
# Regex for a feed request.
|
||||
feed = re.compile(r'/all-this/(\d\d\d\d/\d\d/[^/]+/)?feed/(atom/)?')
|
||||
|
||||
# Regexes for internal and Google search referrers.
|
||||
|
||||
internal = re.compile(r'https?://(www\.)?example\.com.*')
|
||||
google = re.compile(r'https?://(www\.)?google\..*')
|
||||
|
||||
# Regexes for Uptime Monitoring Robots
|
||||
uptimeroboturl = re.compile(r'https?://(www\.)?uptimerobot\..*')
|
||||
uptimerobot = re.compile(r'UptimeRobot')
|
||||
|
||||
# Change Apache log items into Python types.
|
||||
|
||||
def pythonized(d):
|
||||
# Clean up the request.
|
||||
|
||||
d['request'] = d['request'].split()[1]
|
||||
|
||||
# Some dashes become None.
|
||||
|
||||
for k in ('user', 'referrer', 'agent'):
|
||||
if d[k] == '-':
|
||||
d[k] = None
|
||||
|
||||
# The size dash becomes 0.
|
||||
|
||||
if d['size'] == '-':
|
||||
d['size'] = 0
|
||||
else:
|
||||
d['size'] = int(d['size'])
|
||||
|
||||
# Convert the timestamp into a datetime object. Accept the server's time zone.
|
||||
|
||||
(time, zone) = d['time'].split()
|
||||
d['time'] = datetime.strptime(time, '%d/%b/%Y:%H:%M:%S')
|
||||
|
||||
return d
|
||||
|
||||
# Is this hit a page?
|
||||
|
||||
def ispage(hit):
|
||||
# Failures and redirects.
|
||||
|
||||
hit['status'] = int(hit['status'])
|
||||
if hit['status'] < 200 or hit['status'] >= 300:
|
||||
return False
|
||||
|
||||
# Feed requests.
|
||||
|
||||
if feed.search(hit['request']):
|
||||
return False
|
||||
|
||||
# Requests that aren't GET.
|
||||
|
||||
# if (hit['request'])[0:3] != 'GET':
|
||||
# return False
|
||||
|
||||
# Images, sounds, etc.
|
||||
|
||||
if hit['request'].split()[1][-1] != '/':
|
||||
return False
|
||||
|
||||
# Requests that aren't Head type. AKA uptime monitoring
|
||||
|
||||
if (hit['request'])[0:3] == 'HEAD':
|
||||
return False
|
||||
|
||||
# Must be a page.
|
||||
|
||||
return True
|
||||
|
||||
# Is the referrer interesting? Internal and Google referrers are not.
|
||||
def goodref(hit):
|
||||
if hit['referrer']:
|
||||
return not (google.search(hit['referrer'])
|
||||
or internal.search(hit['referrer']))
|
||||
else:
|
||||
return False
|
||||
|
||||
# Is the user agent interesting? An uptime monitoring robot is not.
|
||||
def goodagent(hit):
|
||||
if hit['agent']:
|
||||
return not (uptimerobot.search(hit['agent'])
|
||||
or uptimeroboturl.search(hit['agent']))
|
||||
else:
|
||||
return False
|
||||
|
||||
# create a function which returns the value of a dictionary
|
||||
def keyfunction(k):
|
||||
return d[k]
|
||||
|
||||
# Initialize pages for top IP's
|
||||
|
||||
pages = []
|
||||
|
||||
# Initialize dictionaries for hit counters
|
||||
post_request_dict = {}
|
||||
get_request_dict = {}
|
||||
wp_login_dict = {}
|
||||
wp_cron_dict = {}
|
||||
wp_xmlrpc_dict = {}
|
||||
wp_admin_ajax_dict = {}
|
||||
drupal_login_dict = {}
|
||||
magento_login_dict = {}
|
||||
joomla_login_dict = {}
|
||||
vbulletin_login_dict = {}
|
||||
opencart_login_dict = {}
|
||||
prestashop_login_dict = {}
|
||||
|
||||
# Parse all the lines associated with the day of interest.
|
||||
|
||||
for log in logs:
|
||||
file = os.path.join(path, log)
|
||||
text = open(file, "r")
|
||||
post_request_hit_count = 0
|
||||
get_request_hit_count = 0
|
||||
wp_login_hit_count = 0
|
||||
wp_cron_hit_count = 0
|
||||
wp_xmlrpc_hit_count = 0
|
||||
wp_admin_ajax_hit_count = 0
|
||||
drupal_hit_count = 0
|
||||
magento_hit_count = 0
|
||||
joomla_hit_count = 0
|
||||
vbulletin_hit_count = 0
|
||||
opencart_hit_count = 0
|
||||
prestashop_hit_count = 0
|
||||
for line in text:
|
||||
if apache_day in line:
|
||||
if re.match("(.*)(POST)(.*)", line):
|
||||
post_request_hit_count = post_request_hit_count + 1
|
||||
if re.match("(.*)(GET)(.*)", line):
|
||||
get_request_hit_count = get_request_hit_count + 1
|
||||
if re.match("(.*)(wp-login.php)(.*)", line):
|
||||
wp_login_hit_count = wp_login_hit_count + 1
|
||||
if re.match("(.*)(wp-cron.php)(.*)", line):
|
||||
wp_cron_hit_count = wp_cron_hit_count + 1
|
||||
if re.match("(.*)(xmlrpc.php)(.*)", line):
|
||||
wp_xmlrpc_hit_count = wp_xmlrpc_hit_count + 1
|
||||
if re.match("(.*)(admin-ajax.php)(.*)", line):
|
||||
wp_admin_ajax_hit_count = wp_admin_ajax_hit_count + 1
|
||||
if re.match("(.*)(user/login/)(.*)", line):
|
||||
drupal_hit_count = drupal_hit_count + 1
|
||||
if re.match("(.*)(admin_[a-zA-Z0-9_]*[/admin/index/index])(.*)", line):
|
||||
magento_hit_count = magento_hit_count + 1
|
||||
if re.match("(.*)(/administrator/index.php)(.*)", line):
|
||||
joomla_hit_count = joomla_hit_count + 1
|
||||
if re.match("(.*)(admincp)(.*)", line):
|
||||
vbulletin_hit_count = vbulletin_hit_count + 1
|
||||
if re.match("(.*)(/admin/index.php)(.*)", line):
|
||||
opencart_hit_count = opencart_hit_count + 1
|
||||
if re.match("(.*)(/admin[a-zA-Z0-9_]*$)(.*)", line):
|
||||
prestashop_hit_count = prestashop_hit_count + 1
|
||||
m = pattern.match(line)
|
||||
if m is not None:
|
||||
hit = m.groupdict()
|
||||
else:
|
||||
# print("re.search() returned None")
|
||||
continue
|
||||
# hit = m.groupdict()
|
||||
if ispage(hit):
|
||||
pages.append(pythonized(hit))
|
||||
else:
|
||||
continue
|
||||
# print >> stats_output, log + "|" + line,
|
||||
# print(log + "|" + line, end="", file=stats_output)
|
||||
# print(wp_login_hit_count)
|
||||
log = log.replace('-ssl_log', '', 1)
|
||||
log = log.replace('.access_log', '', 1)
|
||||
|
||||
# wp_login_dict[log] = int(wp_login_hit_count)
|
||||
# wp_cron_dict[log] = int(wp_cron_hit_count)
|
||||
# wp_xmlrpc_dict[log] = int(wp_xmlrpc_hit_count)
|
||||
# wp_admin_ajax_dict[log] = int(wp_admin_ajax_hit_count)
|
||||
|
||||
# Only add hit count to dictionary if not equal to '0'
|
||||
if post_request_hit_count != '0':
|
||||
post_request_dict[log] = int(post_request_hit_count)
|
||||
|
||||
if get_request_hit_count != '0':
|
||||
get_request_dict[log] = int(get_request_hit_count)
|
||||
|
||||
if wp_login_hit_count != '0':
|
||||
wp_login_dict[log] = int(wp_login_hit_count)
|
||||
|
||||
if wp_cron_hit_count != '0':
|
||||
wp_cron_dict[log] = int(wp_cron_hit_count)
|
||||
|
||||
if wp_xmlrpc_hit_count != '0':
|
||||
wp_xmlrpc_dict[log] = int(wp_xmlrpc_hit_count)
|
||||
|
||||
if wp_admin_ajax_hit_count != '0':
|
||||
wp_admin_ajax_dict[log] = int(wp_admin_ajax_hit_count)
|
||||
|
||||
if drupal_hit_count != '0':
|
||||
drupal_login_dict[log] = int(drupal_hit_count)
|
||||
|
||||
if magento_hit_count != '0':
|
||||
magento_login_dict[log] = int(magento_hit_count)
|
||||
|
||||
if joomla_hit_count != '0':
|
||||
joomla_login_dict[log] = int(joomla_hit_count)
|
||||
|
||||
if vbulletin_hit_count != '0':
|
||||
vbulletin_login_dict[log] = int(vbulletin_hit_count)
|
||||
|
||||
if opencart_hit_count != '0':
|
||||
opencart_login_dict[log] = int(opencart_hit_count)
|
||||
|
||||
if prestashop_hit_count != '0':
|
||||
prestashop_login_dict[log] = int(prestashop_hit_count)
|
||||
|
||||
# print(log)
|
||||
# print("Wordpress Logins => " + str(wp_login_hit_count))
|
||||
# print("Wordpress wp-cron => " + str(wp_cron_hit_count))
|
||||
# print("Wordpress xmlrpc => " + str(wp_xmlrpc_hit_count))
|
||||
# print("Wordpress admin-ajax => " + str(wp_admin_ajax_hit_count))
|
||||
# print("===============================================================")
|
||||
text.close()
|
||||
# print(pages, file=stats_output)
|
||||
|
||||
print(' ')
|
||||
print('============================================')
|
||||
print('Snapshot for ' + username)
|
||||
print(time.strftime('%H:%M%p %Z on %b %d, %Y'))
|
||||
if controlpanel == 'Cpanel' or controlpanel == 'CyberPanel':
|
||||
print(controlpanel + " detected")
|
||||
else:
|
||||
print('No control Panel detected')
|
||||
|
||||
print('Accesslog path used: ' + path)
|
||||
# print(dcpumon_current_log)
|
||||
print('============================================')
|
||||
d = post_request_dict
|
||||
# Using dictionary comprehension to find list
|
||||
# keys having value in 0 will be removed from results
|
||||
delete = [key for key in d if d[key] == 0]
|
||||
|
||||
# delete the key
|
||||
for key in delete: del d[key]
|
||||
print(' ')
|
||||
print('''Top POST requests for %s''' % the_day.strftime('%b %d, %Y'))
|
||||
print(' ')
|
||||
# sort by dictionary by the values and print top 10 {key, value} pairs
|
||||
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
|
||||
print(' %5d %s' % (d[key], key))
|
||||
print(' %5d total hits' % sum(dict.values(d)))
|
||||
print('============================================')
|
||||
|
||||
d = get_request_dict
|
||||
# Using dictionary comprehension to find list
|
||||
# keys having value in 0 will be removed from results
|
||||
delete = [key for key in d if d[key] == 0]
|
||||
|
||||
# delete the key
|
||||
for key in delete: del d[key]
|
||||
|
||||
print('''Top GET requests for %s''' % the_day.strftime('%b %d, %Y'))
|
||||
print(' ')
|
||||
# sort by dictionary by the values and print top 10 {key, value} pairs
|
||||
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
|
||||
print(' %5d %s' % (d[key], key))
|
||||
print(' %5d total hits' % sum(dict.values(d)))
|
||||
print('============================================')
|
||||
|
||||
# Show the top 10 pages and the total.
|
||||
print('''
|
||||
Show top 10 pages %s''' % the_day.strftime('%b %d, %Y'))
|
||||
pageviews = Counter(x['request'] for x in pages if goodagent(x))
|
||||
pagestop10 = pageviews.most_common(10)
|
||||
for p in pagestop10:
|
||||
print(' %5d %s' % p[::-1])
|
||||
print(' %5d total' % len(pages))
|
||||
print('============================================')
|
||||
|
||||
# Show the top five referrers.
|
||||
print('''
|
||||
Show top 10 referrers %s''' % the_day.strftime('%b %d, %Y'))
|
||||
referrers = Counter(x['referrer'] for x in pages if goodref(x))
|
||||
referrerstop10 = referrers.most_common(10)
|
||||
for r in referrerstop10:
|
||||
print(' %5d %s' % r[::-1])
|
||||
print(' %5d total' % sum(referrers.values()))
|
||||
print('============================================')
|
||||
# Show the top 10 IPs.
|
||||
print('''
|
||||
Show Top 10 IPs %s''' % the_day.strftime('%b %d, %Y'))
|
||||
iphits = Counter(x['host'] for x in pages if goodagent(x))
|
||||
iptop10 = iphits.most_common(10)
|
||||
for p in iptop10:
|
||||
print(' %5d %s' % p[::-1])
|
||||
print(' %5d total hits' % sum(iphits.values()))
|
||||
print('============================================')
|
||||
|
||||
# CMS Checks
|
||||
|
||||
print(' ')
|
||||
print('CMS Checks')
|
||||
print(' ')
|
||||
|
||||
print('Wordpress Checks')
|
||||
print('============================================')
|
||||
|
||||
d = wp_login_dict
|
||||
# Using dictionary comprehension to find list
|
||||
# keys having value in 0 will be removed from results
|
||||
delete = [key for key in d if d[key] == 0]
|
||||
|
||||
# delete the key
|
||||
for key in delete: del d[key]
|
||||
|
||||
# print(d)
|
||||
|
||||
print('''Wordpress Bruteforce Logins for wp-login.php %s''' % the_day.strftime('%b %d, %Y'))
|
||||
print(' ')
|
||||
# sort by dictionary by the values and print top 10 {key, value} pairs
|
||||
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
|
||||
print(' %5d %s' % (d[key], key))
|
||||
print(' %5d total hits' % sum(dict.values(d)))
|
||||
print(' ')
|
||||
|
||||
d = wp_cron_dict
|
||||
# Using dictionary comprehension to find list
|
||||
# keys having value in 0 will be removed from results
|
||||
delete = [key for key in d if d[key] == 0]
|
||||
|
||||
# delete the key
|
||||
for key in delete: del d[key]
|
||||
|
||||
print('''Wordpress Cron wp-cron.php(virtual cron) checks for %s''' % the_day.strftime('%b %d, %Y'))
|
||||
print(' ')
|
||||
# sort by dictionary by the values and print top 10 {key, value} pairs
|
||||
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
|
||||
print(' %5d %s' % (d[key], key))
|
||||
print(' %5d total hits' % sum(dict.values(d)))
|
||||
print(' ')
|
||||
|
||||
d = wp_xmlrpc_dict
|
||||
# Using dictionary comprehension to find list
|
||||
# keys having value in 0 will be removed from results
|
||||
delete = [key for key in d if d[key] == 0]
|
||||
|
||||
# delete the key
|
||||
for key in delete: del d[key]
|
||||
|
||||
print('''Wordpress XMLRPC Attacks checks for xmlrpc.php for %s''' % the_day.strftime('%b %d, %Y'))
|
||||
print(' ')
|
||||
# sort by dictionary by the values and print top 10 {key, value} pairs
|
||||
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
|
||||
print(' %5d %s' % (d[key], key))
|
||||
print(' %5d total hits' % sum(dict.values(d)))
|
||||
print(' ')
|
||||
|
||||
d = wp_admin_ajax_dict
|
||||
# Using dictionary comprehension to find list
|
||||
# keys having value in 0 will be removed from results
|
||||
delete = [key for key in d if d[key] == 0]
|
||||
|
||||
# delete the key
|
||||
for key in delete: del d[key]
|
||||
|
||||
print('''Wordpress Heartbeat API checks for admin-ajax.php for %s''' % the_day.strftime('%b %d, %Y'))
|
||||
print(' ')
|
||||
# sort by dictionary by the values and print top 10 {key, value} pairs
|
||||
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
|
||||
print(' %5d %s' % (d[key], key))
|
||||
print(' %5d total hits' % sum(dict.values(d)))
|
||||
print('============================================')
|
||||
|
||||
d = drupal_login_dict
|
||||
# Using dictionary comprehension to find list
|
||||
# keys having value in 0 will be removed from results
|
||||
delete = [key for key in d if d[key] == 0]
|
||||
|
||||
# delete the key
|
||||
for key in delete: del d[key]
|
||||
|
||||
print('''Drupal Login Bruteforcing checks for user/login/ for %s''' % the_day.strftime('%b %d, %Y'))
|
||||
print(' ')
|
||||
# sort by dictionary by the values and print top 10 {key, value} pairs
|
||||
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
|
||||
print(' %5d %s' % (d[key], key))
|
||||
print(' %5d total hits' % sum(dict.values(d)))
|
||||
print('============================================')
|
||||
|
||||
d = magento_login_dict
|
||||
# Using dictionary comprehension to find list
|
||||
# keys having value in 0 will be removed from results
|
||||
delete = [key for key in d if d[key] == 0]
|
||||
|
||||
# delete the key
|
||||
for key in delete: del d[key]
|
||||
|
||||
print(
|
||||
'''Magento Login Bruteforcing checks for admin pages /admin_xxxxx/admin/index/index for %s''' % the_day.strftime(
|
||||
'%b %d, %Y'))
|
||||
print(' ')
|
||||
# sort by dictionary by the values and print top 10 {key, value} pairs
|
||||
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
|
||||
print(' %5d %s' % (d[key], key))
|
||||
print(' %5d total hits' % sum(dict.values(d)))
|
||||
print('============================================')
|
||||
|
||||
d = joomla_login_dict
|
||||
# Using dictionary comprehension to find list
|
||||
# keys having value in 0 will be removed from results
|
||||
delete = [key for key in d if d[key] == 0]
|
||||
|
||||
# delete the key
|
||||
for key in delete: del d[key]
|
||||
|
||||
print('''Joomla Login Bruteforcing checks for admin pages /administrator/index.php for %s''' % the_day.strftime(
|
||||
'%b %d, %Y'))
|
||||
print(' ')
|
||||
# sort by dictionary by the values and print top 10 {key, value} pairs
|
||||
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
|
||||
print(' %5d %s' % (d[key], key))
|
||||
print(' %5d total hits' % sum(dict.values(d)))
|
||||
print('============================================')
|
||||
|
||||
d = vbulletin_login_dict
|
||||
# Using dictionary comprehension to find list
|
||||
# keys having value in 0 will be removed from results
|
||||
delete = [key for key in d if d[key] == 0]
|
||||
|
||||
# delete the key
|
||||
for key in delete: del d[key]
|
||||
|
||||
print('''vBulletin Login Bruteforcing checks for admin pages admincp for %s''' % the_day.strftime('%b %d, %Y'))
|
||||
print(' ')
|
||||
# sort by dictionary by the values and print top 10 {key, value} pairs
|
||||
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
|
||||
print(' %5d %s' % (d[key], key))
|
||||
print(' %5d total hits' % sum(dict.values(d)))
|
||||
print('============================================')
|
||||
|
||||
d = opencart_login_dict
|
||||
# Using dictionary comprehension to find list
|
||||
# keys having value in 0 will be removed from results
|
||||
delete = [key for key in d if d[key] == 0]
|
||||
|
||||
# delete the key
|
||||
for key in delete: del d[key]
|
||||
|
||||
print('''Opencart Login Bruteforcing checks for admin pages /admin/index.php for %s''' % the_day.strftime(
|
||||
'%b %d, %Y'))
|
||||
print(' ')
|
||||
# sort by dictionary by the values and print top 10 {key, value} pairs
|
||||
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
|
||||
print(' %5d %s' % (d[key], key))
|
||||
print(' %5d total hits' % sum(dict.values(d)))
|
||||
print('============================================')
|
||||
|
||||
d = prestashop_login_dict
|
||||
# Using dictionary comprehension to find list
|
||||
# keys having value in 0 will be removed from results
|
||||
delete = [key for key in d if d[key] == 0]
|
||||
|
||||
# delete the key
|
||||
for key in delete: del d[key]
|
||||
|
||||
print('''Prestashop Login Bruteforcing checks for admin pages /adminxxxx for %s''' % the_day.strftime('%b %d, %Y'))
|
||||
print(' ')
|
||||
# sort by dictionary by the values and print top 10 {key, value} pairs
|
||||
for key in sorted(d, key=keyfunction, reverse=True)[:10]:
|
||||
print(' %5d %s' % (d[key], key))
|
||||
print(' %5d total hits' % sum(dict.values(d)))
|
||||
print('============================================')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
158
CPScripts/access-logparser.sh
Normal file
158
CPScripts/access-logparser.sh
Normal file
@@ -0,0 +1,158 @@
|
||||
#!/bin/bash
|
||||
## Author: Michael Ramsey
|
||||
## Objective Find A Cyberpanel/cPanel Users Dom/Access logs Stats for last 5 days for all of their domains. v2
|
||||
## https://gitlab.com/mikeramsey/access-log-parser
|
||||
## How to use.
|
||||
# ./access-logparser.sh username
|
||||
#./access-logparser.sh exampleuserbob
|
||||
#
|
||||
##bash <(curl -s https://gitlab.com/mikeramsey/access-log-parser/-/raw/master/access-logparser.sh || wget -qO - https://gitlab.com/mikeramsey/access-log-parser/-/raw/master/access-logparser.sh) exampleuserbob;
|
||||
##
|
||||
Username=$1
|
||||
|
||||
#Detect Control panel
|
||||
if [ -f /usr/local/cpanel/cpanel ]; then
|
||||
# Cpanel check for /usr/local/cpanel/cpanel -V
|
||||
ControlPanel="cpanel"
|
||||
datetimeDcpumon=$(date +"%Y/%b/%d") # 2019/Feb/15
|
||||
#Current Dcpumon file
|
||||
DcpumonCurrentLOG="/var/log/dcpumon/${datetimeDcpumon}" # /var/log/dcpumon/2019/Feb/15
|
||||
#Setup datetimeDcpumonLast5_array
|
||||
declare -a datetimeDcpumonLast5_array=($(date +"%Y/%b/%d") $(date --date='1 day ago' +"%Y/%b/%d") $(date --date='2 days ago' +"%Y/%b/%d") $(date --date='3 days ago' +"%Y/%b/%d") $(date --date='4 days ago' +"%Y/%b/%d")); #for DATE in "${datetimeDcpumonLast5_array[@]}"; do echo $DATE; done;
|
||||
|
||||
user_homedir="/home/${Username}"
|
||||
user_accesslogs="/home/${Username}/logs/"
|
||||
domlogs_path="/usr/local/apache/domlogs/${Username}/"
|
||||
acesslog_sed="-ssl_log"
|
||||
|
||||
elif [ -f /usr/bin/cyberpanel ]; then
|
||||
# CyberPanel check /usr/bin/cyberpanel
|
||||
ControlPanel="cyberpanel"
|
||||
|
||||
#Get users homedir path
|
||||
user_homedir=$(sudo egrep "^${Username}:" /etc/passwd | cut -d: -f6)
|
||||
domlogs_path="${user_homedir}/logs/"
|
||||
acesslog_sed=".access_log"
|
||||
|
||||
else
|
||||
echo "Not able to detect Control panel. Unsupported Control Panel exiting now"
|
||||
exit 1;
|
||||
fi
|
||||
echo "=============================================================";
|
||||
echo "$ControlPanel Control Panel Detected"
|
||||
echo "User Homedirectory: ${user_homedir}"
|
||||
echo "User Domlogs Path: ${domlogs_path}"
|
||||
echo "=============================================================";
|
||||
echo "";
|
||||
#Domlog Date array for past 5 days
|
||||
declare -a datetimeDomLast5_array=($(date +"%d/%b/%Y") $(date --date='1 day ago' +"%d/%b/%Y") $(date --date='2 days ago' +"%d/%b/%Y") $(date --date='3 days ago' +"%d/%b/%Y") $(date --date='4 days ago' +"%d/%b/%Y")); #for DATE in "${datetimeDomLast5_array[@]}"; do echo $DATE; done;
|
||||
|
||||
|
||||
Now=$(date +"%Y-%m-%d_%T")
|
||||
|
||||
user_Snapshot="${Username}-Snapshot_${Now}.txt";
|
||||
|
||||
#create logfile in user's homedirectory.
|
||||
#sudo touch "$user_CyberpanelSnapshot"
|
||||
|
||||
#chown logfile to user
|
||||
#sudo chown ${Username}:${Username} "$user_CyberpanelSnapshot";
|
||||
|
||||
|
||||
main_function() {
|
||||
|
||||
if [ "${ControlPanel}" == "cpanel" ] ;
|
||||
|
||||
then
|
||||
for DATE in "${datetimeDcpumonLast5_array[@]}"; do
|
||||
echo "=============================================================";
|
||||
echo "Find $Username user's highest CPU use processes via Dcpumon Logs for $DATE";
|
||||
sudo grep "$Username" /var/log/dcpumon/"${DATE}";
|
||||
done; echo "";
|
||||
echo "For more information about Dcpumon(Daily Process Logs) see https://docs.cpanel.net/whm/server-status/daily-process-log/82/"
|
||||
echo "============================================================="
|
||||
echo "";
|
||||
else
|
||||
#echo "The DcpumonCurrentLOG '$DcpumonCurrentLOG' was not found. Not running Dcpumon stats"
|
||||
echo "";
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Web Traffic Stats Check";
|
||||
|
||||
echo "";
|
||||
for DATE in "${datetimeDomLast5_array[@]}"; do
|
||||
echo "=============================================================";
|
||||
echo "HTTP Dom Logs POST Requests for ${DATE} for $Username";
|
||||
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $1}' | cut -d: -f1|sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"| sort | uniq -c | sort -rn | head
|
||||
echo ""
|
||||
echo "HTTP Dom Logs GET Requests for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep GET | awk '{print $1}' | cut -d: -f1 |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"| sort | uniq -c | sort -rn | head
|
||||
echo ""
|
||||
echo "HTTP Dom Logs Top 10 bot/crawler requests per domain name for ${DATE}"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep -Ei 'crawl|bot|spider|yahoo|bing|google'| awk '{print $1}' | cut -d: -f1|sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"| sort | uniq -c | sort -rn | head
|
||||
echo ""
|
||||
echo "HTTP Dom Logs top ten IPs for ${DATE} for $Username"
|
||||
|
||||
command=$(sudo grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $1}'|sed -e 's/^[^=:]*[=:]//' -e 's|"||g' | sort | uniq -c | sort -rn | head| column -t);readarray -t iparray < <( echo "${command}" | tr '/' '\n'); echo ""; for IP in "${iparray[@]}"; do echo "$IP"; done; echo ""; echo "Show unique IP's with whois IP, Country,and ISP"; echo ""; for IP in "${iparray[@]}"; do IP=$(echo "$IP" |grep -Eo '([0-9]{1,3}[.]){3}[0-9]{1,3}|(*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:)))(%.+)?\s*)'); whois -h whois.cymru.com " -c -p $IP"|cut -d"|" -f 2,4,5|grep -Ev 'IP|whois.cymru.com'; done
|
||||
|
||||
echo ""
|
||||
echo "Checking the IPs that Have Hit the Server Most and What Site they were hitting:"
|
||||
sudo grep -rs "$DATE" ${domlogs_path} | awk {'print $1'} |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed:| |g"| sort | uniq -c | sort -n | tail -10| sort -rn| column -t
|
||||
echo ""
|
||||
echo "Checking the Top Hits Per Site Per IP:"
|
||||
sudo grep -rs "$DATE" ${domlogs_path} | awk {'print $1,$6,$7'} |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed:| |g"| sort | uniq -c | sort -n | tail -10| sort -rn| column -t
|
||||
echo ""
|
||||
echo "HTTP Dom Logs find the top number of uri's being requested for ${DATE}"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep POST | awk '{print $7}' | cut -d: -f2 |sed "s|$domlogs_path||g"| sort | uniq -c | sort -rn | head| column -t
|
||||
echo ""
|
||||
echo "";
|
||||
echo "View HTTP requests per hour for $Username";
|
||||
sudo grep -r "$DATE" ${domlogs_path} | cut -d[ -f2 | cut -d] -f1 | awk -F: '{print $2":00"}' | sort -n | uniq -c| column -t
|
||||
echo ""
|
||||
echo "CMS Checks"
|
||||
echo ""
|
||||
echo "Wordpress Checks"
|
||||
echo "Wordpress Login Bruteforcing checks for wp-login.php for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep wp-login.php | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Wordpress Cron wp-cron.php(virtual cron) checks for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep wp-cron.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Wordpress XMLRPC Attacks checks for xmlrpc.php for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep xmlrpc.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Wordpress Heartbeat API checks for admin-ajax.php for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep admin-ajax.php| cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn;
|
||||
echo ""
|
||||
echo "CMS Bruteforce Checks"
|
||||
echo "Drupal Login Bruteforcing checks for user/login/ for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep -E "user/login/" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Magento Login Bruteforcing checks for admin pages /admin_xxxxx/admin/index/index for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep -E "admin_[a-zA-Z0-9_]*[/admin/index/index]" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Joomla Login Bruteforcing checks for admin pages /administrator/index.php for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep -E "/administrator/index.php" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "vBulletin Login Bruteforcing checks for admin pages admincp for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep -E "admincp" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Opencart Login Bruteforcing checks for admin pages /admin/index.php for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep -E "/admin/index.php" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
echo "Prestashop Login Bruteforcing checks for admin pages /adminxxxx for ${DATE} for $Username"
|
||||
sudo grep -r "$DATE" ${domlogs_path} | grep -E "/admin[a-zA-Z0-9_]*$" | cut -f 1 -d ":" |sed -e "s|$domlogs_path||g" -e 's|"||g' -e "s|$acesslog_sed||g" -e "s|$Username/||g"|awk {'print $1,$6,$7'} | sort | uniq -c | sort -n|tail| sort -rn
|
||||
echo ""
|
||||
|
||||
|
||||
done;
|
||||
echo "============================================================="
|
||||
|
||||
|
||||
echo "Contents have been saved to ${user_Snapshot}"
|
||||
}
|
||||
|
||||
# log everything, but also output to stdout
|
||||
main_function 2>&1 | tee -a "${user_Snapshot}"
|
||||
455
CPScripts/fixperms.sh
Normal file
455
CPScripts/fixperms.sh
Normal file
@@ -0,0 +1,455 @@
|
||||
#!/usr/bin/env bash
|
||||
## Author: Michael Ramsey
|
||||
## Objective Fix permissions issues on CyberPanel/cPanel/Plesk for a linux user or users
|
||||
# https://gitlab.com/wizardassistantscripts/fixperms
|
||||
#
|
||||
# Forked from https://github.com/PeachFlame/cPanel-fixperms
|
||||
#
|
||||
# Plesk portion credits too
|
||||
# https://www.orware.com/blog/tips-and-how-tos/plesk/correct-httpdocs-permissions
|
||||
# https://support.plesk.com/hc/en-us/articles/115001969889--BUG-plesk-repair-fs-doesn-t-set-correct-owner-inside-httpdocs
|
||||
|
||||
## How to use.
|
||||
# wget https://gitlab.com/wizardassistantscripts/fixperms/-/raw/master/fixperms.sh ; bash fixperms.sh username
|
||||
#
|
||||
# wget https://gitlab.com/wizardassistantscripts/fixperms/-/raw/master/fixperms.sh ; bash fixperms.sh exampleuserbob
|
||||
#
|
||||
# Or once of
|
||||
## bash <(curl -s https://gitlab.com/wizardassistantscripts/fixperms/-/raw/master/fixperms.sh || wget -qO - https://gitlab.com/wizardassistantscripts/fixperms/-/raw/master/fixperms.sh) exampleuserbob;
|
||||
#
|
||||
# Permanent Install for reuse via the below
|
||||
# wget -O /usr/bin/fixperms https://gitlab.com/wizardassistantscripts/fixperms/-/raw/master/fixperms.sh; chmod +x /usr/bin/fixperms;
|
||||
#
|
||||
# Then
|
||||
# fixperms -v -a Username
|
||||
# fixperms -v -all
|
||||
# Username=$1
|
||||
|
||||
|
||||
#Detect Control panel
|
||||
if [ -f /usr/local/cpanel/cpanel ]; then
|
||||
# Cpanel check for /usr/local/cpanel/cpanel -V
|
||||
ControlPanel="cpanel"
|
||||
#user_homedir="/home/${Username}"
|
||||
|
||||
|
||||
elif [ -f /usr/bin/cyberpanel ]; then
|
||||
# CyberPanel check /usr/bin/cyberpanel
|
||||
ControlPanel="cyberpanel"
|
||||
#Get users homedir path
|
||||
#user_homedir=$(grep -E "^${Username}:" /etc/passwd | cut -d: -f6)
|
||||
|
||||
elif [ -f /usr/local/psa/core.version ]; then
|
||||
# Plesk check /usr/local/psa/core.version
|
||||
ControlPanel="plesk"
|
||||
|
||||
#Get users homedir path
|
||||
#user_homedir=$(grep -E "^${Username}:" /etc/passwd | cut -d: -f6)
|
||||
|
||||
|
||||
else
|
||||
echo "Not able to detect Control panel. Unsupported Control Panel exiting now"
|
||||
exit 1;
|
||||
fi
|
||||
echo "=============================================================";
|
||||
echo "$ControlPanel Control Panel Detected"
|
||||
echo "=============================================================";
|
||||
echo "";
|
||||
|
||||
|
||||
|
||||
# Set verbose to null
|
||||
verbose=""
|
||||
|
||||
|
||||
#Print the help text
|
||||
helptext () {
|
||||
tput bold
|
||||
tput setaf 2
|
||||
echo "Fix perms script help:"
|
||||
echo "Sets file/directory permissions to match suPHP and FastCGI schemes"
|
||||
echo "USAGE: fixperms [options] -a account_name"
|
||||
echo "-------"
|
||||
echo "Options:"
|
||||
echo "-h or --help: print this screen and exit"
|
||||
echo "-v: verbose output"
|
||||
echo "-all: run on all Cyberpanel accounts"
|
||||
echo "--account or -a: specify a Cyberpanel/cPanel/Plesk account"
|
||||
# echo "--domain or -d: specify a Cyberpanel domain"
|
||||
tput sgr0
|
||||
exit 0
|
||||
}
|
||||
|
||||
#Detect OS
|
||||
if [ -f /etc/os-release ]; then
|
||||
# freedesktop.org and systemd
|
||||
. /etc/os-release
|
||||
OS=$NAME
|
||||
VER=$VERSION_ID
|
||||
elif type lsb_release >/dev/null 2>&1; then
|
||||
# linuxbase.org
|
||||
OS=$(lsb_release -si)
|
||||
VER=$(lsb_release -sr)
|
||||
elif [ -f /etc/lsb-release ]; then
|
||||
# For some versions of Debian/Ubuntu without lsb_release command
|
||||
. /etc/lsb-release
|
||||
OS=$DISTRIB_ID
|
||||
VER=$DISTRIB_RELEASE
|
||||
elif [ -f /etc/debian_version ]; then
|
||||
# Older Debian/Ubuntu/etc.
|
||||
OS=Debian
|
||||
VER=$(cat /etc/debian_version)
|
||||
elif [ -f /etc/SuSe-release ]; then
|
||||
# Older SuSE/etc.
|
||||
...
|
||||
elif [ -f /etc/redhat-release ]; then
|
||||
# Older Red Hat, CentOS, etc.
|
||||
...
|
||||
else
|
||||
# Fall back to uname, e.g. "Linux <version>", also works for BSD, etc.
|
||||
OS=$(uname -s)
|
||||
VER=$(uname -r)
|
||||
fi
|
||||
|
||||
|
||||
|
||||
#### Cyberpanel Section
|
||||
|
||||
# fix mailperms
|
||||
fixmailperms_cyberpanel () {
|
||||
tput bold
|
||||
tput setaf 4
|
||||
echo "Fixing mailperms...."
|
||||
tput sgr0
|
||||
#Fix perms of /home/vmail
|
||||
chown -R vmail:vmail /home/vmail
|
||||
chmod 755 /home/vmail
|
||||
find /home/vmail -type d -exec chmod 0755 {} \;
|
||||
find /home/vmail -type f -exec chmod 0640 {} \;
|
||||
echo "Finished fixing mailperms...."
|
||||
|
||||
}
|
||||
|
||||
# Main workhorse, fix perms per account passed to it
|
||||
fixperms_cyberpanel () {
|
||||
|
||||
#Get account from what is passed to the function
|
||||
account=$1
|
||||
|
||||
#Make sure account isn't blank
|
||||
if [ -z "$account" ]
|
||||
then
|
||||
tput bold
|
||||
tput setaf 1
|
||||
echo "Need an account name!"
|
||||
tput sgr0
|
||||
helptext
|
||||
#Else, start doing work
|
||||
else
|
||||
|
||||
# Get linux user from Domain
|
||||
domain_username=$(grep -E "/${1}:" /etc/passwd | cut -d: -f1)
|
||||
if id "$1" >/dev/null 2>&1; then
|
||||
echo "$1 exists"
|
||||
elif id "$domain_username" >/dev/null 2>&1; then
|
||||
echo "Found user: $domain_username from domain: $1"
|
||||
echo "$domain_username exists"
|
||||
account=$domain_username
|
||||
else
|
||||
echo "user does not exist"
|
||||
fi
|
||||
|
||||
|
||||
#Get the account's homedir
|
||||
HOMEDIR=$(grep -E "^${account}:" /etc/passwd | cut -d: -f6)
|
||||
echo "User Homedirectory: ${HOMEDIR}"
|
||||
tput bold
|
||||
tput setaf 4
|
||||
echo "Fixing perms for $account:"
|
||||
tput setaf 3
|
||||
if [ -d "$HOMEDIR/.cagefs" ]; then
|
||||
chmod 775 "$HOMEDIR"/.cagefs
|
||||
chmod 700 "$HOMEDIR"/.cagefs/tmp
|
||||
chmod 700 "$HOMEDIR"/.cagefs/var
|
||||
chmod 777 "$HOMEDIR"/.cagefs/cache
|
||||
chmod 777 "$HOMEDIR"/.cagefs/run
|
||||
fi
|
||||
echo "------------------------"
|
||||
tput setaf 4
|
||||
echo "Fixing website files...."
|
||||
tput sgr0
|
||||
|
||||
|
||||
#Fix individual files in public_html
|
||||
find "$HOMEDIR"/public_html -type d -exec chmod "$verbose" 755 {} \;
|
||||
find "$HOMEDIR"/public_html -type f -print0 | xargs -d$'\n' -r chmod "$verbose" 644
|
||||
find "$HOMEDIR"/public_html -name '*.cgi' -print0 -o -name '*.pl' | xargs -0 -r chmod "$verbose" 755
|
||||
chown $verbose -R "$account":"$account" "$HOMEDIR"/public_html/*
|
||||
# Hidden files test support: https://serverfault.com/a/156481
|
||||
chown "$verbose" -R "$account":"$account" "$HOMEDIR"/public_html/.[^.]*
|
||||
find "$HOMEDIR"/* -name .htaccess -exec chown "$verbose" "$account"."$account" {} \;
|
||||
|
||||
tput bold
|
||||
tput setaf 4
|
||||
echo "Fixing public_html...."
|
||||
tput sgr0
|
||||
#Fix perms of public_html itself
|
||||
chown "$verbose" "$account":nobody "$HOMEDIR"/public_html
|
||||
chmod "$verbose" 755 "$HOMEDIR"/public_html
|
||||
|
||||
tput bold
|
||||
tput setaf 4
|
||||
echo "Fixing logs...."
|
||||
tput sgr0
|
||||
#Fix perms of $HOMEDIR/logs
|
||||
chown "$verbose" nobody:"$account" "$HOMEDIR"/logs
|
||||
chmod "$verbose" 750 "$HOMEDIR"/logs
|
||||
find "$HOMEDIR"/logs/* -name '*.access_log' -exec chown "$verbose" nobody."$account" {} \;
|
||||
|
||||
|
||||
|
||||
#Fix subdomains that lie outside of public_html
|
||||
#tput setaf 3
|
||||
#tput bold
|
||||
#echo "------------------------"
|
||||
#tput setaf 4
|
||||
#echo "Fixing any domains with a document root outside of public_html...."
|
||||
#for SUBDOMAIN in $(grep -i documentroot /var/cpanel/userdata/$account/* | grep -v '.cache\|_SSL' | awk '{print $2}' | grep -v public_html)
|
||||
#do
|
||||
#tput bold
|
||||
#tput setaf 4
|
||||
#echo "Fixing sub/addon domain document root $SUBDOMAIN...."
|
||||
#tput sgr0
|
||||
#find $SUBDOMAIN -type d -exec chmod $verbose 755 {} \;
|
||||
#find $SUBDOMAIN -type f -print0 | xargs -d$'\n' -r chmod $verbose 644
|
||||
#find $SUBDOMAIN -name '*.cgi' -o -name '*.pl' | xargs -r chmod $verbose 755
|
||||
#chown $verbose -R $account:$account $SUBDOMAIN
|
||||
#find $SUBDOMAIN -name .htaccess -exec chown $verbose $account.$account {} \;
|
||||
#done
|
||||
|
||||
#Finished
|
||||
tput bold
|
||||
tput setaf 3
|
||||
echo "Finished!"
|
||||
echo "------------------------"
|
||||
printf "\n\n"
|
||||
tput sgr0
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
#########cPanel
|
||||
# Main workhorse, fix perms per account passed to it
|
||||
fixperms_cpanel () {
|
||||
|
||||
#Get account from what is passed to the function
|
||||
account=$1
|
||||
|
||||
#Check account against cPanel users file
|
||||
if ! grep "$account" /var/cpanel/users/*
|
||||
then
|
||||
tput bold
|
||||
tput setaf 1
|
||||
echo "Invalid cPanel account"
|
||||
tput sgr0
|
||||
exit 0
|
||||
fi
|
||||
|
||||
#Make sure account isn't blank
|
||||
if [ -z "$account" ]
|
||||
then
|
||||
tput bold
|
||||
tput setaf 1
|
||||
echo "Need an account name!"
|
||||
tput sgr0
|
||||
helptext
|
||||
#Else, start doing work
|
||||
else
|
||||
|
||||
#Get the account's homedir
|
||||
HOMEDIR=$(grep -E "^${account}:" /etc/passwd | cut -d: -f6)
|
||||
echo "User Homedirectory: ${HOMEDIR}"
|
||||
tput bold
|
||||
tput setaf 4
|
||||
echo "Fixing perms for $account:"
|
||||
tput setaf 3
|
||||
if [ -d "$HOMEDIR/.cagefs" ]; then
|
||||
chmod 775 "$HOMEDIR"/.cagefs
|
||||
chmod 700 "$HOMEDIR"/.cagefs/tmp
|
||||
chmod 700 "$HOMEDIR"/.cagefs/var
|
||||
chmod 777 "$HOMEDIR"/.cagefs/cache
|
||||
chmod 777 "$HOMEDIR"/.cagefs/run
|
||||
fi
|
||||
echo "------------------------"
|
||||
tput setaf 4
|
||||
echo "Fixing website files...."
|
||||
tput sgr0
|
||||
|
||||
|
||||
|
||||
#Fix individual files in public_html
|
||||
find "$HOMEDIR"/public_html -type d -exec chmod "$verbose" 755 {} \;
|
||||
find "$HOMEDIR"/public_html -type f -print0 | xargs -0 -d$'\n' -r chmod "$verbose" 644
|
||||
find "$HOMEDIR"/public_html -name '*.cgi' -print0 -o -name '*.pl' | xargs -0 -r chmod "$verbose" 755
|
||||
chown $verbose -R "$account":"$account" "$HOMEDIR"/public_html/*
|
||||
# fix hidden files and folders like .well-known/ with root or other user perms
|
||||
chown "$verbose" -R "$account":"$account" "$HOMEDIR"/public_html/.[^.]*
|
||||
find "$HOMEDIR"/* -name .htaccess -exec chown "$verbose" "$account"."$account" {} \;
|
||||
|
||||
tput bold
|
||||
tput setaf 4
|
||||
echo "Fixing public_html...."
|
||||
tput sgr0
|
||||
#Fix perms of public_html itself
|
||||
chown "$verbose" "$account":nobody "$HOMEDIR"/public_html
|
||||
chmod "$verbose" 750 "$HOMEDIR"/public_html
|
||||
|
||||
#Fix subdomains that lie outside of public_html
|
||||
tput setaf 3
|
||||
tput bold
|
||||
echo "------------------------"
|
||||
tput setaf 4
|
||||
echo "Fixing any domains with a document root outside of public_html...."
|
||||
for SUBDOMAIN in $(grep -i documentroot /var/cpanel/userdata/"$account"/* | grep -v '.cache\|_SSL' | awk '{print $2}' | grep -v public_html)
|
||||
do
|
||||
tput bold
|
||||
tput setaf 4
|
||||
echo "Fixing sub/addon domain document root $SUBDOMAIN...."
|
||||
tput sgr0
|
||||
find "$SUBDOMAIN" -type d -exec chmod "$verbose" 755 {} \;
|
||||
find "$SUBDOMAIN" -type f -print0 | xargs -0 -d$'\n' -r chmod "$verbose" 644
|
||||
find "$SUBDOMAIN" -name '*.cgi' -print0 -o -name '*.pl' | xargs -0 -r chmod "$verbose" 755
|
||||
chown "$verbose" -R "$account":"$account" "$SUBDOMAIN"
|
||||
chmod "$verbose" 755 "$SUBDOMAIN"
|
||||
find "$SUBDOMAIN" -name .htaccess -exec chown "$verbose" "$account"."$account" {} \;
|
||||
done
|
||||
|
||||
#Finished
|
||||
tput bold
|
||||
tput setaf 3
|
||||
echo "Finished!"
|
||||
echo "------------------------"
|
||||
printf "\n\n"
|
||||
tput sgr0
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
###################################
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
##################################
|
||||
|
||||
fixperms () {
|
||||
Username=$1
|
||||
if [ "${ControlPanel}" == "cpanel" ] ; then
|
||||
|
||||
fixperms_cpanel "${Username}"
|
||||
# Fix users mailperms
|
||||
tput bold
|
||||
tput setaf 4
|
||||
echo "Fixing Mailperms...."
|
||||
tput sgr0
|
||||
/scripts/mailperm --verbose "${Username}"
|
||||
#Finished
|
||||
tput bold
|
||||
tput setaf 3
|
||||
echo "Finished!"
|
||||
echo "------------------------"
|
||||
printf "\n\n"
|
||||
tput sgr0
|
||||
|
||||
elif [ "${ControlPanel}" == "cyberpanel" ] ; then
|
||||
|
||||
fixperms_cyberpanel "${Username}"
|
||||
fixmailperms_cyberpanel
|
||||
|
||||
|
||||
elif [ "${ControlPanel}" == "plesk" ] ; then
|
||||
#Get users homedir path
|
||||
user_homedir=$(grep -E "^${Username}:" /etc/passwd | cut -d: -f6)
|
||||
echo "User Homedirectory: ${user_homedir}"
|
||||
echo "Resetting perms/ownership for ${user_homedir}/httpdocs"
|
||||
sudo chown -R "${Username}":psacln "${user_homedir}"/httpdocs
|
||||
sudo chown "${Username}":psaserv "${user_homedir}"/httpdocs
|
||||
|
||||
fi
|
||||
}
|
||||
|
||||
all () {
|
||||
|
||||
if [ "${ControlPanel}" == "cpanel" ] ; then
|
||||
|
||||
for user in $(cut -d: -f1 /etc/domainusers)
|
||||
do
|
||||
fixperms_cpanel "$user"
|
||||
done
|
||||
# Fix all users mailperms
|
||||
/scripts/mailperm --verbose
|
||||
|
||||
elif [ "${ControlPanel}" == "cyberpanel" ] ; then
|
||||
|
||||
if [[ $OS = 'CentOS Linux' ]] ; then
|
||||
for user in $(getent passwd | awk -F: '5001<$3 && $3<6000 {print $1}' |grep -v spamd)
|
||||
do
|
||||
fixperms_cyberpanel "$user"
|
||||
done
|
||||
fixmailperms_cyberpanel
|
||||
fi
|
||||
|
||||
if [[ $OS = 'Ubuntu' ]] ; then
|
||||
for user in $(getent passwd | awk -F: '1001<$3 && $3<2000 {print $1}')
|
||||
do
|
||||
fixperms_cyberpanel "$user"
|
||||
done
|
||||
fixmailperms_cyberpanel
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
||||
#Main function, switches options passed to it
|
||||
case "$1" in
|
||||
|
||||
-h) helptext
|
||||
;;
|
||||
--help) helptext
|
||||
;;
|
||||
-v) verbose="-v"
|
||||
|
||||
case "$2" in
|
||||
|
||||
-all) all
|
||||
;;
|
||||
--account) fixperms "$3"
|
||||
;;
|
||||
-a) fixperms "$3"
|
||||
;;
|
||||
*) tput bold
|
||||
tput setaf 1
|
||||
echo "Invalid Option!"
|
||||
helptext
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
-all) all
|
||||
;;
|
||||
--account) fixperms "$2"
|
||||
;;
|
||||
-a) fixperms "$2"
|
||||
;;
|
||||
*)
|
||||
tput bold
|
||||
tput setaf 1
|
||||
echo "Invalid Option!"
|
||||
helptext
|
||||
;;
|
||||
esac
|
||||
390
CPScripts/mailscannerinstaller.sh
Normal file
390
CPScripts/mailscannerinstaller.sh
Normal file
@@ -0,0 +1,390 @@
|
||||
#!/bin/bash
|
||||
#systemctl stop firewalld
|
||||
|
||||
check_return() {
|
||||
#check previous command result , 0 = ok , non-0 = something wrong.
|
||||
if [[ $? -eq "0" ]]; then
|
||||
:
|
||||
else
|
||||
echo -e "\ncommand failed, exiting..."
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
echo 'backup configs'
|
||||
cp /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf-bak_$(date '+%Y-%m-%d_%H_%M:%S')
|
||||
cp /etc/postfix/master.cf /etc/postfix/master.cf-bak_$(date '+%Y-%m-%d_%H_%M:%S')
|
||||
cp /etc/postfix/main.cf /etc/postfix/main.cf-bak_$(date '+%Y-%m-%d_%H_%M:%S')
|
||||
cp /etc/dovecot/dovecot-sql.conf.ext /etc/dovecot/dovecot-sql.conf.ext-bak_$(date '+%Y-%m-%d_%H_%M:%S')
|
||||
|
||||
ZONE=$(firewall-cmd --get-default-zone)
|
||||
firewall-cmd --zone=$ZONE --add-port=4190/tcp --permanent
|
||||
systemctl stop firewalld
|
||||
|
||||
echo 'Stop CSF'
|
||||
csf -x
|
||||
|
||||
MAILSCANNER=/etc/MailScanner
|
||||
|
||||
if [ -d $MAILSCANNER ]; then
|
||||
echo "MailScanner found. If you wish to reinstall then remove the package and revert"
|
||||
echo "Postfix back to its original config at /etc/postfix/main.cf and remove"
|
||||
echo "/etc/MailScanner and /usr/share/MailScanner directories"
|
||||
exit
|
||||
fi
|
||||
|
||||
### Check SpamAssasin before moving forward
|
||||
|
||||
DIR=/etc/mail/spamassassin
|
||||
|
||||
if [ -d "$DIR" ]; then
|
||||
sa-update
|
||||
else
|
||||
echo "Please install SpamAssasin through the CyberPanel interface before proceeding"
|
||||
exit
|
||||
fi
|
||||
|
||||
### OS Detection
|
||||
Server_OS=""
|
||||
Server_OS_Version=""
|
||||
if grep -q -E "CentOS Linux 7|CentOS Linux 8" /etc/os-release ; then
|
||||
Server_OS="CentOS"
|
||||
elif grep -q "AlmaLinux-8" /etc/os-release ; then
|
||||
Server_OS="AlmaLinux"
|
||||
elif grep -q -E "CloudLinux 7|CloudLinux 8" /etc/os-release ; then
|
||||
Server_OS="CloudLinux"
|
||||
elif grep -q -E "Rocky Linux" /etc/os-release ; then
|
||||
Server_OS="RockyLinux"
|
||||
elif grep -q -E "Ubuntu 18.04|Ubuntu 20.04|Ubuntu 20.10|Ubuntu 22.04" /etc/os-release ; then
|
||||
Server_OS="Ubuntu"
|
||||
elif grep -q -E "openEuler 20.03|openEuler 22.03" /etc/os-release ; then
|
||||
Server_OS="openEuler"
|
||||
else
|
||||
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, CentOS 7, CentOS 8, AlmaLinux 8, RockyLinux 8, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03...\n"
|
||||
exit
|
||||
fi
|
||||
|
||||
Server_OS_Version=$(grep VERSION_ID /etc/os-release | awk -F[=,] '{print $2}' | tr -d \" | head -c2 | tr -d . )
|
||||
|
||||
echo -e "System: $Server_OS $Server_OS_Version detected...\n"
|
||||
|
||||
if [[ $Server_OS = "CloudLinux" ]] || [[ "$Server_OS" = "AlmaLinux" ]] || [[ "$Server_OS" = "RockyLinux" ]] ; then
|
||||
Server_OS="CentOS"
|
||||
#CloudLinux gives version id like 7.8, 7.9, so cut it to show first number only
|
||||
#treat CloudLinux, Rocky and Alma as CentOS
|
||||
fi
|
||||
|
||||
if [[ $Server_OS = "CentOS" ]] && [[ "$Server_OS_Version" = "7" ]] ; then
|
||||
|
||||
setenforce 0
|
||||
yum install -y perl yum-utils perl-CPAN
|
||||
yum install -y gcc cpp perl bzip2 zip make patch automake rpm-build perl-Archive-Zip perl-Filesys-Df perl-OLE-Storage_Lite perl-Sys-Hostname-Long perl-Sys-SigAction perl-Net-CIDR perl-DBI perl-MIME-tools perl-DBD-SQLite binutils glibc-devel perl-Filesys-Df zlib unzip zlib-devel wget mlocate clamav "perl(DBD::mysql)"
|
||||
|
||||
rpm -Uvh https://forensics.cert.org/centos/cert/7/x86_64/unrar-5.4.0-1.el7.x86_64.rpm
|
||||
export PERL_MM_USE_DEFAULT=1
|
||||
curl -L https://cpanmin.us | perl - App::cpanminus
|
||||
perl -MCPAN -e 'install Encoding::FixLatin'
|
||||
perl -MCPAN -e 'install Digest::SHA1'
|
||||
perl -MCPAN -e 'install Geo::IP'
|
||||
perl -MCPAN -e 'install Razor2::Client::Agent'
|
||||
perl -MCPAN -e 'install Net::Patricia'
|
||||
|
||||
freshclam -v
|
||||
|
||||
elif [[ $Server_OS = "CentOS" ]] && [[ "$Server_OS_Version" = "8" ]] ; then
|
||||
|
||||
setenforce 0
|
||||
yum install -y perl yum-utils perl-CPAN
|
||||
dnf --enablerepo=powertools install -y perl-IO-stringy
|
||||
dnf --enablerepo=PowerTools install -y perl-IO-stringy
|
||||
yum install -y gcc cpp perl bzip2 zip make patch automake rpm-build perl-Archive-Zip perl-Filesys-Df perl-OLE-Storage_Lite perl-Net-CIDR perl-DBI perl-MIME-tools perl-DBD-SQLite binutils glibc-devel perl-Filesys-Df zlib unzip zlib-devel wget mlocate clamav clamav-update "perl(DBD::mysql)"
|
||||
|
||||
rpm -Uvh https://forensics.cert.org/centos/cert/8/x86_64/unrar-5.4.0-1.el8.x86_64.rpm
|
||||
|
||||
export PERL_MM_USE_DEFAULT=1
|
||||
curl -L https://cpanmin.us | perl - App::cpanminus
|
||||
|
||||
perl -MCPAN -e 'install Encoding::FixLatin'
|
||||
perl -MCPAN -e 'install Digest::SHA1'
|
||||
perl -MCPAN -e 'install Geo::IP'
|
||||
perl -MCPAN -e 'install Razor2::Client::Agent'
|
||||
perl -MCPAN -e 'install Sys::Hostname::Long'
|
||||
perl -MCPAN -e 'install Sys::SigAction'
|
||||
|
||||
freshclam -v
|
||||
|
||||
elif [ "$CLNVERSION" = "ID=\"cloudlinux\"" ]; then
|
||||
|
||||
setenforce 0
|
||||
yum install -y perl yum-utils perl-CPAN
|
||||
yum install -y gcc cpp perl bzip2 zip make patch automake rpm-build perl-Archive-Zip perl-Filesys-Df perl-OLE-Storage_Lite perl-Sys-Hostname-Long perl-Sys-SigAction perl-Net-CIDR perl-DBI perl-MIME-tools perl-DBD-SQLite binutils glibc-devel perl-Filesys-Df zlib unzip zlib-devel wget mlocate clamav "perl(DBD::mysql)"
|
||||
|
||||
rpm -Uvh https://forensics.cert.org/centos/cert/7/x86_64/unrar-5.4.0-1.el7.x86_64.rpm
|
||||
export PERL_MM_USE_DEFAULT=1
|
||||
curl -L https://cpanmin.us | perl - App::cpanminus
|
||||
perl -MCPAN -e 'install Encoding::FixLatin'
|
||||
perl -MCPAN -e 'install Digest::SHA1'
|
||||
perl -MCPAN -e 'install Geo::IP'
|
||||
perl -MCPAN -e 'install Razor2::Client::Agent'
|
||||
perl -MCPAN -e 'install Net::Patricia'
|
||||
|
||||
freshclam -v
|
||||
|
||||
elif [[ $Server_OS = "Ubuntu" ]]; then
|
||||
|
||||
apt-get install -y libmysqlclient-dev
|
||||
|
||||
apt-get install -y cpanminus gcc perl bzip2 zip make patch automake rpm libarchive-zip-perl libfilesys-df-perl libole-storage-lite-perl libsys-hostname-long-perl libsys-sigaction-perl libregexp-common-net-cidr-perl libmime-tools-perl libdbd-sqlite3-perl binutils build-essential libfilesys-df-perl zlib1g unzip mlocate clamav libdbd-mysql-perl unrar libclamav-dev libclamav-client-perl libclamunrar9
|
||||
|
||||
cpanm Encoding::FixLatin
|
||||
cpanm Digest::SHA1
|
||||
cpanm Geo::IP
|
||||
cpanm Razor2::Client::Agent
|
||||
cpanm Net::Patricia
|
||||
cpanm Net::CIDR
|
||||
|
||||
sudo systemctl stop clamav-freshclam.service
|
||||
|
||||
freshclam
|
||||
|
||||
sudo systemctl start clamav-freshclam.service
|
||||
|
||||
fi
|
||||
|
||||
echo "header_checks = regexp:/etc/postfix/header_checks" >>/etc/postfix/main.cf
|
||||
echo "/^Received:/ HOLD" >>/etc/postfix/header_checks
|
||||
|
||||
systemctl restart postfix
|
||||
|
||||
if [[ $Server_OS = "Ubuntu" ]]; then
|
||||
wget https://github.com/MailScanner/v5/releases/download/5.4.4-1/MailScanner-5.4.4-1.noarch.deb
|
||||
dpkg -i *.noarch.deb
|
||||
|
||||
mkdir /var/run/MailScanner
|
||||
mkdir /var/lock/subsys
|
||||
mkdir /var/lock/subsys/MailScanner
|
||||
chown -R postfix:postfix /var/run/MailScanner
|
||||
chown -R postfix:postfix /var/lock/subsys/MailScanner
|
||||
chown -R postfix:postfix /var/spool/MailScanner
|
||||
|
||||
elif [[ $Server_OS = "CentOS" ]]; then
|
||||
wget https://github.com/MailScanner/v5/releases/download/5.4.4-1/MailScanner-5.4.4-1.rhel.noarch.rpm
|
||||
rpm -Uvh *.rhel.noarch.rpm
|
||||
elif [ "$OS" = "NAME=\"CloudLinux\"" ]; then
|
||||
wget https://github.com/MailScanner/v5/releases/download/5.3.3-1/MailScanner-5.3.3-1.rhel.noarch.rpm
|
||||
rpm -Uvh *.rhel.noarch.rpm
|
||||
fi
|
||||
|
||||
mkdir /var/spool/MailScanner/spamassassin
|
||||
|
||||
chown postfix.mtagroup /var/spool/MailScanner/spamassassin
|
||||
chown root.mtagroup /var/spool/MailScanner/incoming/
|
||||
chown postfix.mtagroup /var/spool/MailScanner/milterin
|
||||
chown postfix.mtagroup /var/spool/MailScanner/milterout
|
||||
chown postfix.mtagroup /var/spool/postfix/hold
|
||||
chown postfix.mtagroup /var/spool/postfix/incoming
|
||||
usermod -a -G mtagroup nobody
|
||||
|
||||
chmod g+rx /var/spool/postfix/incoming
|
||||
chmod g+rx /var/spool/postfix/hold
|
||||
chmod -R 0775 /var/spool/postfix/incoming
|
||||
chmod -R 0775 /var/spool/postfix/hold
|
||||
|
||||
sed -i 's/^Run As User =.*/& postfix/' /etc/MailScanner/MailScanner.conf
|
||||
sed -i 's/^Run As Group =.*/& postfix/' /etc/MailScanner/MailScanner.conf
|
||||
sed -i 's/^Incoming Queue Dir =.*/Incoming Queue Dir = \/var\/spool\/postfix\/hold/' /etc/MailScanner/MailScanner.conf
|
||||
sed -i 's/^Outgoing Queue Dir =.*/Outgoing Queue Dir = \/var\/spool\/postfix\/incoming/' /etc/MailScanner/MailScanner.conf
|
||||
sed -i 's/^MTA =.*/MTA = postfix/' /etc/MailScanner/MailScanner.conf
|
||||
sed -i 's/^Quarantine User =.*/& postfix/' /etc/MailScanner/MailScanner.conf
|
||||
sed -i 's/^Quarantine Group =.*/& mtagroup/' /etc/MailScanner/MailScanner.conf
|
||||
sed -i 's/^Quarantine Permissions =.*/Quarantine Permissions = 640/' /etc/MailScanner/MailScanner.conf
|
||||
sed -i 's/^Virus Scanners =.*/Virus Scanners = clamav/' /etc/MailScanner/MailScanner.conf
|
||||
sed -i 's/^Is Definitely Not Spam =.*/Is Definitely Not Spam = \&SQLWhitelist/' /etc/MailScanner/MailScanner.conf
|
||||
sed -i 's/^Is Definitely Spam =.*/Is Definitely Spam = \&SQLBlacklist/' /etc/MailScanner/MailScanner.conf
|
||||
sed -i 's/^SpamAssassin User State Dir =.*/& \/var\/spool\/MailScanner\/spamassassin/' /etc/MailScanner/MailScanner.conf
|
||||
sed -i 's/^Always Looked Up Last =.*/Always Looked Up Last = \&MailWatchLogging/' /etc/MailScanner/MailScanner.conf
|
||||
sed -i 's/^Quarantine Whole Message =.*/Quarantine Whole Message = yes/' /etc/MailScanner/MailScanner.conf
|
||||
sed -i 's/^Spam List =.*/Spam List = SBL + XBL/' /etc/MailScanner/MailScanner.conf
|
||||
sed -i 's/^Sign Clean Messages =.*/Sign Clean Messages = no/' /etc/MailScanner/MailScanner.conf
|
||||
|
||||
mkdir /usr/local/CyberCP/public/mailwatch
|
||||
|
||||
cd /usr/local/CyberCP/public/mailwatch
|
||||
|
||||
git clone --depth=1 https://github.com/mailwatch/MailWatch.git --branch 1.2 --single-branch
|
||||
|
||||
mv /usr/local/CyberCP/public/mailwatch/MailWatch/* /usr/local/CyberCP/public/mailwatch/
|
||||
|
||||
PASSWORD=$(cat /etc/cyberpanel/mysqlPassword)
|
||||
USER=root
|
||||
DATABASE=mailscanner
|
||||
ADMINPASS=$(cat /etc/cyberpanel/adminPass)
|
||||
|
||||
### Fix a bug in MailWatch SQL File
|
||||
|
||||
sed -i 's/char(512)/char(255)/g' /usr/local/CyberCP/public/mailwatch/create.sql
|
||||
|
||||
##
|
||||
|
||||
mysql -u${USER} -p${PASSWORD} <"/usr/local/CyberCP/public/mailwatch/create.sql"
|
||||
mysql -u${USER} -p${PASSWORD} -e "use mailscanner"
|
||||
mysql -u${USER} -D${DATABASE} -p${PASSWORD} -e "GRANT ALL ON mailscanner.* TO root@localhost IDENTIFIED BY '${PASSWORD}';"
|
||||
mysql -u${USER} -D${DATABASE} -p${PASSWORD} -e "FLUSH PRIVILEGES;"
|
||||
mysql -u${USER} -D${DATABASE} -p${PASSWORD} -e "INSERT INTO mailscanner.users SET username = 'admin', password = MD5('${ADMINPASS}'), fullname = 'admin', type = 'A';"
|
||||
|
||||
cp /usr/local/CyberCP/public/mailwatch/mailscanner/conf.php.example /usr/local/CyberCP/public/mailwatch/mailscanner/conf.php
|
||||
|
||||
sed -i "s/^define('DB_USER',.*/define('DB_USER','root');/" /usr/local/CyberCP/public/mailwatch/mailscanner/conf.php
|
||||
sed -i "s/^define('DB_PASS',.*/define('DB_PASS','${PASSWORD}');/" /usr/local/CyberCP/public/mailwatch/mailscanner/conf.php
|
||||
sed -i "s/^define('MAILWATCH_HOME',.*/define(\'MAILWATCH_HOME\', \'\/usr\/local\/CyberCP\/public\/mailwatch\/mailscanner');/" /usr/local/CyberCP/public/mailwatch/mailscanner/conf.php
|
||||
|
||||
MSDEFAULT=/etc/MailScanner/defaults
|
||||
if [ -f "$MSDEFAULT" ]; then
|
||||
sed -i 's/^run_mailscanner=.*/run_mailscanner=1/' /etc/MailScanner/defaults
|
||||
elif [ ! -f "$MSDEFAULT" ]; then
|
||||
touch /etc/MailScanner/defaults
|
||||
echo "run_mailscanner=1" >>/etc/MailScanner/defaults
|
||||
fi
|
||||
|
||||
cp /usr/local/CyberCP/public/mailwatch/MailScanner_perl_scripts/MailWatchConf.pm /usr/share/MailScanner/perl/custom/
|
||||
sed -i 's/^my (\$db_user) = .*/my (\$db_user) = \x27'${USER}'\x27;/' /usr/share/MailScanner/perl/custom/MailWatchConf.pm
|
||||
sed -i 's/^my (\$db_pass) = .*/my (\$db_pass) = \x27'${PASSWORD}'\x27;/' /usr/share/MailScanner/perl/custom/MailWatchConf.pm
|
||||
ln -s /usr/local/CyberCP/public/mailwatch/MailScanner_perl_scripts/MailWatch.pm /usr/share/MailScanner/perl/custom
|
||||
ln -s /usr/local/CyberCP/public/mailwatch/MailScanner_perl_scripts/SQLBlackWhiteList.pm /usr/share/MailScanner/perl/custom
|
||||
ln -s /usr/local/CyberCP/public/mailwatch/MailScanner_perl_scripts/SQLSpamSettings.pm /usr/share/MailScanner/perl/custom
|
||||
sed -i "s/^\$pathToFunctions =.*/\$pathToFunctions = '\/usr\/local\/CyberCP\/public\/mailwatch\/mailscanner\/functions.php';/" /usr/local/CyberCP/public/mailwatch/upgrade.php
|
||||
|
||||
/usr/local/lsws/lsphp72/bin/php /usr/local/CyberCP/public/mailwatch/upgrade.php
|
||||
systemctl enable mailscanner
|
||||
systemctl restart mailscanner
|
||||
|
||||
IPADDRESS=$(cat /etc/cyberpanel/machineIP)
|
||||
|
||||
### Furhter onwards is sieve configurations
|
||||
|
||||
#echo 'Setting up spamassassin and sieve to deliver spam to Junk folder by default'
|
||||
##echo "If you wish mailscanner/spamassassin to send spam email to a spam folder please follow the tutorial on the Cyberpanel Website"
|
||||
#echo 'Fix protocols'
|
||||
#sed -i 's/^protocols =.*/protocols = imap pop3 lmtp sieve/g' /etc/dovecot/dovecot.conf
|
||||
#
|
||||
#sed -i "s|^user_query.*|user_query = SELECT '5000' as uid, '5000' as gid, '/home/vmail/%d/%n' as home,mail FROM e_users WHERE email='%u';|g" /etc/dovecot/dovecot-sql.conf.ext
|
||||
#
|
||||
#if [ "$OS" = "NAME=\"Ubuntu\"" ]; then
|
||||
# if [ "$UBUNTUVERSION" = "VERSION_ID=\"18.04\"" ]; then
|
||||
# apt-get install -y dovecot-managesieved dovecot-sieve dovecot-lmtpd net-tools pflogsumm
|
||||
# elif [ "$UBUNTUVERSION" = "VERSION_ID=\"20.04\"" ]; then
|
||||
# apt-get install -y libmysqlclient-dev
|
||||
# sed -e '/deb/ s/^#*/#/' -i /etc/apt/sources.list.d/dovecot.list
|
||||
# apt install -y dovecot-lmtpd dovecot-managesieved dovecot-sieve net-tools pflogsumm
|
||||
# fi
|
||||
#
|
||||
#elif [ "$CENTOSVERSION" = "VERSION_ID=\"7\"" ]; then
|
||||
#
|
||||
# yum install -y nano net-tools dovecot-pigeonhole postfix-perl-scripts
|
||||
#
|
||||
#elif [ "$CENTOSVERSION" = "VERSION_ID=\"8\"" ]; then
|
||||
#
|
||||
# rpm -Uvh http://mirror.ghettoforge.org/distributions/gf/el/8/gf/x86_64/gf-release-8-11.gf.el8.noarch.rpm
|
||||
# dnf --enablerepo=gf-plus upgrade -y dovecot23*
|
||||
# dnf --enablerepo=gf-plus install -y dovecot23-pigeonhole
|
||||
# dnf install -y net-tools postfix-perl-scripts
|
||||
#
|
||||
#elif [ "$CLNVERSION" = "ID=\"cloudlinux\"" ]; then
|
||||
# yum install -y nano net-tools dovecot-pigeonhole postfix-perl-scripts
|
||||
#fi
|
||||
#
|
||||
## Create Sieve files
|
||||
#mkdir -p /etc/dovecot/sieve/global
|
||||
#touch /var/log/{dovecot-lda-errors.log,dovecot-lda.log}
|
||||
#touch /var/log/{dovecot-sieve-errors.log,dovecot-sieve.log}
|
||||
#touch /var/log/{dovecot-lmtp-errors.log,dovecot-lmtp.log}
|
||||
#touch /etc/dovecot/sieve/default.sieve
|
||||
#chown vmail: -R /etc/dovecot/sieve
|
||||
#chown vmail:mail /var/log/dovecot-*
|
||||
#
|
||||
#echo 'Create Sieve Default spam to Junk rule'
|
||||
#cat >>/etc/dovecot/sieve/default.sieve <<EOL
|
||||
#require "fileinto";
|
||||
#if header :contains "X-Spam-Flag" "YES" {
|
||||
# fileinto "INBOX.Junk E-mail";
|
||||
#}
|
||||
#EOL
|
||||
#
|
||||
#echo "Adding Sieve to /etc/dovecot/dovecot.conf"
|
||||
#cat >>/etc/dovecot/dovecot.conf <<EOL
|
||||
#
|
||||
#service managesieve-login {
|
||||
# inet_listener sieve {
|
||||
# port = 4190
|
||||
# }
|
||||
#}
|
||||
#service managesieve {
|
||||
#}
|
||||
#protocol sieve {
|
||||
# managesieve_max_line_length = 65536
|
||||
# managesieve_implementation_string = dovecot
|
||||
# log_path = /var/log/dovecot-sieve-errors.log
|
||||
# info_log_path = /var/log/dovecot-sieve.log
|
||||
#}
|
||||
#plugin {
|
||||
#sieve = /home/vmail/%d/%n/dovecot.sieve
|
||||
#sieve_global_path = /etc/dovecot/sieve/default.sieve
|
||||
#sieve_dir = /home/vmail/%d/%n/sieve
|
||||
#sieve_global_dir = /etc/dovecot/sieve/global/
|
||||
#}
|
||||
#protocol lda {
|
||||
# mail_plugins = $mail_plugins sieve quota
|
||||
# postmaster_address = postmaster@example.com
|
||||
# hostname = server.example.com
|
||||
# auth_socket_path = /var/run/dovecot/auth-master
|
||||
# log_path = /var/log/dovecot-lda-errors.log
|
||||
# info_log_path = /var/log/dovecot-lda.log
|
||||
#}
|
||||
#protocol lmtp {
|
||||
# mail_plugins = $mail_plugins sieve quota
|
||||
# log_path = /var/log/dovecot-lmtp-errors.log
|
||||
# info_log_path = /var/log/dovecot-lmtp.log
|
||||
#}
|
||||
#EOL
|
||||
#
|
||||
#hostname=$(hostname)
|
||||
#
|
||||
#echo 'Fix postmaster email in sieve'
|
||||
#postmaster_address=$(grep postmaster_address /etc/dovecot/dovecot.conf | sed 's/.*=//' | sed -e 's/^[ \t]*//' | sort -u)
|
||||
#
|
||||
#sed -i "s|postmaster@example.com|$postmaster_address|g" /etc/dovecot/dovecot.conf
|
||||
#sed -i "s|server.example.com|$hostname|g" /etc/dovecot/dovecot.conf
|
||||
#sed -i "s|postmaster@example.com|$postmaster_address|g" /etc/dovecot/dovecot.conf
|
||||
#
|
||||
##Sieve the global spam filter
|
||||
#sievec /etc/dovecot/sieve/default.sieve
|
||||
#
|
||||
##Sieve the global spam filter
|
||||
#sievec /etc/dovecot/sieve/default.sieve
|
||||
#
|
||||
#if [ "$OS" = "NAME=\"Ubuntu\"" ]; then
|
||||
# sed -i 's|^spamassassin.*|spamassassin unix - n n - - pipe flags=DROhu user=vmail:vmail argv=/usr/bin/spamc -f -e /usr/lib/dovecot/deliver -f ${sender} -d ${user}@${nexthop}|g' /etc/postfix/master.cf
|
||||
#
|
||||
#elif [ "$OS" = "NAME=\"CentOS Linux\"" ]; then
|
||||
# sed -i 's|^spamassassin.*|spamassassin unix - n n - - pipe flags=DROhu user=vmail:vmail argv=/usr/bin/spamc -f -e /usr/libexec/dovecot/deliver -f ${sender} -d ${user}@${nexthop}|g' /etc/postfix/master.cf
|
||||
#
|
||||
#elif [ "$OS" = "NAME=\"CloudLinux\"" ]; then
|
||||
# sed -i 's|^spamassassin.*|spamassassin unix - n n - - pipe flags=DROhu user=vmail:vmail argv=/usr/bin/spamc -f -e /usr/libexec/dovecot/deliver -f ${sender} -d ${user}@${nexthop}|g' /etc/postfix/master.cf
|
||||
#
|
||||
#fi
|
||||
|
||||
echo 'Restart and check services are up'
|
||||
systemctl restart dovecot && systemctl restart postfix && systemctl restart spamassassin && systemctl restart mailscanner
|
||||
|
||||
csf -e
|
||||
|
||||
echo "MailScanner successfully installed. MailWatch successfully installed."
|
||||
echo "Visit https://${IPADDRESS}:8090/mailwatch/mailscanner"
|
||||
echo "Username: admin"
|
||||
echo "Password: ${ADMINPASS}"
|
||||
#echo "If you wish mailscanner/spamassassin to send spam email to a spam folder please follow the tutorial on the Cyberpanel Website"
|
||||
echo "Firewalld is stopped. Either enable, install CSF or use an alternative!"
|
||||
echo "Optional cpan/cpanm modules are available for MailScanner. Cronjobs and further postfix tools are available for MailWatch"
|
||||
echo "See https://www.mailwatch.org and https://docs.mailwatch.org/install/optional-setup.html"
|
||||
exit
|
||||
57
CPScripts/mailscanneruninstaller.sh
Normal file
57
CPScripts/mailscanneruninstaller.sh
Normal file
@@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
## Uninstall Mailscanner CyberPanel
|
||||
|
||||
### OS Detection
|
||||
Server_OS=""
|
||||
Server_OS_Version=""
|
||||
if grep -q -E "CentOS Linux 7|CentOS Linux 8" /etc/os-release ; then
|
||||
Server_OS="CentOS"
|
||||
elif grep -q "AlmaLinux-8" /etc/os-release ; then
|
||||
Server_OS="AlmaLinux"
|
||||
elif grep -q -E "CloudLinux 7|CloudLinux 8" /etc/os-release ; then
|
||||
Server_OS="CloudLinux"
|
||||
elif grep -q -E "Rocky Linux" /etc/os-release ; then
|
||||
Server_OS="RockyLinux"
|
||||
elif grep -q -E "Ubuntu 18.04|Ubuntu 20.04|Ubuntu 20.10|Ubuntu 22.04" /etc/os-release ; then
|
||||
Server_OS="Ubuntu"
|
||||
elif grep -q -E "openEuler 20.03|openEuler 22.03" /etc/os-release ; then
|
||||
Server_OS="openEuler"
|
||||
else
|
||||
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, CentOS 7, CentOS 8, AlmaLinux 8, RockyLinux 8, CloudLinux 7, CloudLinux 8, openEuler 20.03, openEuler 22.03...\n"
|
||||
exit
|
||||
fi
|
||||
|
||||
Server_OS_Version=$(grep VERSION_ID /etc/os-release | awk -F[=,] '{print $2}' | tr -d \" | head -c2 | tr -d . )
|
||||
|
||||
echo -e "System: $Server_OS $Server_OS_Version detected...\n"
|
||||
|
||||
if [[ $Server_OS = "CloudLinux" ]] || [[ "$Server_OS" = "AlmaLinux" ]] || [[ "$Server_OS" = "RockyLinux" ]] ; then
|
||||
Server_OS="CentOS"
|
||||
#CloudLinux gives version id like 7.8, 7.9, so cut it to show first number only
|
||||
#treat CloudLinux, Rocky and Alma as CentOS
|
||||
fi
|
||||
|
||||
systemctl stop mailscanner
|
||||
|
||||
|
||||
if [[ $Server_OS = "CentOS" ]] && [[ "$Server_OS_Version" = "7" ]] ; then
|
||||
|
||||
yum remove -y MailScanner
|
||||
|
||||
elif [[ $Server_OS = "CentOS" ]] && [[ "$Server_OS_Version" = "8" ]] ; then
|
||||
|
||||
yum remove -y MailScanner
|
||||
|
||||
elif [[ $Server_OS = "Ubuntu" ]]; then
|
||||
|
||||
apt purge -y mailscanner
|
||||
|
||||
fi
|
||||
|
||||
sed -i 's/\/^Received:\/ HOLD/\/^Received:\/ IGNORE/g' /etc/postfix/header_checks
|
||||
rm -rf /etc/MailScanner
|
||||
rm -rf /usr/share/MailScanner
|
||||
rm -rf /usr/local/CyberCP/public/mailwatch
|
||||
|
||||
systemctl restart postfix dovecot
|
||||
59
CPScripts/setup_php_sessions.sh
Normal file
59
CPScripts/setup_php_sessions.sh
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bash
|
||||
## Author: Michael Ramsey
|
||||
## Objective Fix session issues on CyberPanel and standardized session paths.
|
||||
# Fixes #430
|
||||
# https://github.com/usmannasir/cyberpanel/issues/430
|
||||
|
||||
|
||||
# Create the session path directories and chmod it for security to 1733 like the existing one is.
|
||||
|
||||
for version in $(ls /usr/local/lsws|grep lsphp);
|
||||
do
|
||||
mkdir -p "/var/lib/lsphp/session/$version"
|
||||
chmod -R 1733 "/var/lib/lsphp/session/$version"
|
||||
done
|
||||
|
||||
|
||||
YUM_CMD=$(which yum 2> /dev/null)
|
||||
APT_GET_CMD=$(which apt-get 2> /dev/null)
|
||||
|
||||
if [[ -n $YUM_CMD ]]; then
|
||||
# Centos
|
||||
for version in $(ls /usr/local/lsws|grep lsphp); do echo ""; echo "PHP $version"; sed -i -e "s|^;session.save_path.*|session.save_path = '/var/lib/lsphp/session/${version}'|g" -e "s|^session.save_path.*|session.save_path = '/var/lib/lsphp/session/${version}'|g" /usr/local/lsws/${version}/etc/php.ini; /usr/local/lsws/${version}/bin/php -i |grep -Ei 'session.save_path' && echo "" ; done; service lsws restart; killall lsphp;
|
||||
|
||||
|
||||
|
||||
elif [[ -n $APT_GET_CMD ]]; then
|
||||
# Ubuntu
|
||||
for phpver in $(ls -1 /usr/local/lsws/ |grep lsphp | sed 's/lsphp//g') ; do echo ""; echo "LSPHP $phpver" ; lsphpver=$(echo $phpver | sed 's/^\(.\{1\}\)/\1./'); sed -i -e "s|^;session.save_path.*|session.save_path = '/var/lib/lsphp/session/lsphp${phpver}'|g" -e "s|^session.save_path.*|session.save_path = '/var/lib/lsphp/session/lsphp${phpver}'|g" /usr/local/lsws/lsphp${phpver}/etc/php/${lsphpver}/litespeed/php.ini ; /usr/local/lsws/lsphp${phpver}/bin/php -i |grep -Ei 'session.save_path' && echo "" ; done; service lsws restart; killall lsphp;
|
||||
|
||||
else
|
||||
echo "error can't install required packages. Unsupported OS"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
|
||||
# Setup a cron to clear stuff older then session.gc_maxlifetime currently set in the php.ini for each version
|
||||
|
||||
# Create cron file if missing.
|
||||
if [[ ! -e /usr/local/CyberCP/bin/cleansessions ]]; then
|
||||
touch /usr/local/CyberCP/bin/cleansessions
|
||||
chmod +x /usr/local/CyberCP/bin/cleansessions
|
||||
cat >> /usr/local/CyberCP/bin/cleansessions <<"EOL"
|
||||
#!/bin/bash
|
||||
for version in $(ls /usr/local/lsws|grep lsphp); do echo ""; echo "PHP $version"; session_time=$(/usr/local/lsws/${version}/bin/php -i |grep -Ei 'session.gc_maxlifetime'| grep -Eo "[[:digit:]]+"|sort -u); find -O3 "/var/lib/lsphp/session/${version}" -ignore_readdir_race -depth -mindepth 1 -name 'sess_*' -type f -cmin 120 -delete; done
|
||||
EOL
|
||||
|
||||
fi
|
||||
|
||||
# Create crontab only if not exist
|
||||
echo "Installing PHP Session cleaning cron"
|
||||
command="/usr/local/CyberCP/bin/cleansessions >/dev/null 2>&1"
|
||||
job="09,39 * * * * $command"
|
||||
cat <(grep -i -v "$command" <(crontab -l)) <(echo "$job") | crontab -
|
||||
|
||||
echo "Checking cleansessions file"
|
||||
cat /usr/local/CyberCP/bin/cleansessions
|
||||
|
||||
# Set to a 4 hour default as the 24 min default is kinda low and logs people out too often and as a global default in shared scenario its hard for clients to know how to override this while working in their admin area backends etc.
|
||||
grep -Eilr '^memory_limit' --include=\*php.ini /usr/local/lsws/lsphp* | xargs sed -i -e "s/^session.gc_maxlifetime.*/session.gc_maxlifetime = '14400'/g"
|
||||
126
CPScripts/spamassassin_tojunk_installer.sh
Normal file
126
CPScripts/spamassassin_tojunk_installer.sh
Normal file
@@ -0,0 +1,126 @@
|
||||
#!/bin/bash
|
||||
# SpamAssassin Setup Spam to Junk folder. Should be called after the main SpamAssassin install part completes or mapped to an optional button to install. Personally think this should be a default part of the SpamAssassin installation.
|
||||
|
||||
echo 'backup configs';
|
||||
cp /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf-bak_$(date '+%Y-%m-%d_%H_%M:%S');
|
||||
cp /etc/postfix/master.cf /etc/postfix/master.cf-bak_$(date '+%Y-%m-%d_%H_%M:%S');
|
||||
cp /etc/postfix/main.cf /etc/postfix/main.cf-bak_$(date '+%Y-%m-%d_%H_%M:%S');
|
||||
cp /etc/dovecot/dovecot-sql.conf.ext /etc/dovecot/dovecot-sql.conf.ext-bak_$(date '+%Y-%m-%d_%H_%M:%S')
|
||||
|
||||
|
||||
echo 'Setting up spamassassin and sieve to deliver spam to Junk folder by default'
|
||||
echo 'Fix protocols'
|
||||
sed -i 's/^protocols =.*/protocols = imap pop3 lmtp sieve/g' /etc/dovecot/dovecot.conf
|
||||
|
||||
|
||||
sed -i "s|^user_query.*|user_query = SELECT '5000' as uid, '5000' as gid, '/home/vmail/%d/%n' as home,mail FROM e_users WHERE email='%u';|g" /etc/dovecot/dovecot-sql.conf.ext
|
||||
|
||||
if [ "$OS" = "NAME=\"Ubuntu\"" ];then
|
||||
if [ "$UBUNTUVERSION" = "VERSION_ID=\"18.04\"" ];then
|
||||
apt-get install -y dovecot-managesieved dovecot-sieve dovecot-lmtpd net-tools pflogsumm
|
||||
elif [ "$UBUNTUVERSION" = "VERSION_ID=\"20.04\"" ];then
|
||||
apt-get install -y libmysqlclient-dev
|
||||
sed -e '/deb/ s/^#*/#/' -i /etc/apt/sources.list.d/dovecot.list
|
||||
apt install -y dovecot-lmtpd dovecot-managesieved dovecot-sieve net-tools pflogsumm
|
||||
fi
|
||||
|
||||
elif [ "$CENTOSVERSION" = "VERSION_ID=\"7\"" ];then
|
||||
|
||||
yum install -y nano net-tools dovecot-pigeonhole postfix-perl-scripts
|
||||
|
||||
elif [ "$CENTOSVERSION" = "VERSION_ID=\"8\"" ];then
|
||||
|
||||
rpm -Uvh http://mirror.ghettoforge.org/distributions/gf/el/8/gf/x86_64/gf-release-8-11.gf.el8.noarch.rpm
|
||||
dnf --enablerepo=gf-plus upgrade -y dovecot23*
|
||||
dnf --enablerepo=gf-plus install -y dovecot23-pigeonhole
|
||||
dnf install -y net-tools postfix-perl-scripts
|
||||
|
||||
elif [ "$CLNVERSION" = "ID=\"cloudlinux\"" ];then
|
||||
|
||||
yum install -y nano net-tools dovecot-pigeonhole postfix-perl-scripts
|
||||
fi
|
||||
|
||||
|
||||
# Create Sieve files
|
||||
mkdir -p /etc/dovecot/sieve/global
|
||||
touch /var/log/{dovecot-lda-errors.log,dovecot-lda.log}
|
||||
touch /var/log/{dovecot-sieve-errors.log,dovecot-sieve.log}
|
||||
touch /var/log/{dovecot-lmtp-errors.log,dovecot-lmtp.log}
|
||||
touch /etc/dovecot/sieve/default.sieve
|
||||
chown vmail: -R /etc/dovecot/sieve
|
||||
chown vmail:mail /var/log/dovecot-*
|
||||
|
||||
echo 'Create Sieve Default spam to Junk rule'
|
||||
cat >> /etc/dovecot/sieve/default.sieve <<EOL
|
||||
require "fileinto";
|
||||
if header :contains "X-Spam-Flag" "YES" {
|
||||
fileinto "INBOX.Junk E-mail";
|
||||
}
|
||||
EOL
|
||||
|
||||
|
||||
echo "Adding Sieve to /etc/dovecot/dovecot.conf"
|
||||
cat >> /etc/dovecot/dovecot.conf <<EOL
|
||||
service managesieve-login {
|
||||
inet_listener sieve {
|
||||
port = 4190
|
||||
}
|
||||
}
|
||||
service managesieve {
|
||||
}
|
||||
protocol sieve {
|
||||
managesieve_max_line_length = 65536
|
||||
managesieve_implementation_string = dovecot
|
||||
log_path = /var/log/dovecot-sieve-errors.log
|
||||
info_log_path = /var/log/dovecot-sieve.log
|
||||
}
|
||||
plugin {
|
||||
sieve = /home/vmail/%d/%n/dovecot.sieve
|
||||
sieve_global_path = /etc/dovecot/sieve/default.sieve
|
||||
sieve_dir = /home/vmail/%d/%n/sieve
|
||||
sieve_global_dir = /etc/dovecot/sieve/global/
|
||||
}
|
||||
protocol lda {
|
||||
mail_plugins = $mail_plugins sieve quota
|
||||
postmaster_address = postmaster@example.com
|
||||
hostname = server.example.com
|
||||
auth_socket_path = /var/run/dovecot/auth-master
|
||||
log_path = /var/log/dovecot-lda-errors.log
|
||||
info_log_path = /var/log/dovecot-lda.log
|
||||
}
|
||||
protocol lmtp {
|
||||
mail_plugins = $mail_plugins sieve quota
|
||||
log_path = /var/log/dovecot-lmtp-errors.log
|
||||
info_log_path = /var/log/dovecot-lmtp.log
|
||||
}
|
||||
EOL
|
||||
|
||||
hostname=$(hostname);
|
||||
|
||||
echo 'Fix postmaster email in sieve'
|
||||
postmaster_address=$(grep postmaster_address /etc/dovecot/dovecot.conf | sed 's/.*=//' |sed -e 's/^[ \t]*//'| sort -u)
|
||||
|
||||
sed -i "s|postmaster@example.com|$postmaster_address|g" /etc/dovecot/dovecot.conf
|
||||
sed -i "s|server.example.com|$hostname|g" /etc/dovecot/dovecot.conf
|
||||
sed -i "s|postmaster@example.com|$postmaster_address|g" /etc/dovecot/dovecot.conf
|
||||
|
||||
#Sieve the global spam filter
|
||||
sievec /etc/dovecot/sieve/default.sieve
|
||||
|
||||
#Sieve the global spam filter
|
||||
sievec /etc/dovecot/sieve/default.sieve
|
||||
|
||||
if [ "$OS" = "NAME=\"Ubuntu\"" ];then
|
||||
sed -i 's|^spamassassin.*|spamassassin unix - n n - - pipe flags=DROhu user=vmail:vmail argv=/usr/bin/spamc -f -e /usr/lib/dovecot/deliver -f ${sender} -d ${user}@${nexthop}|g' /etc/postfix/master.cf
|
||||
|
||||
elif [ "$OS" = "NAME=\"CentOS Linux\"" ];then
|
||||
sed -i 's|^spamassassin.*|spamassassin unix - n n - - pipe flags=DROhu user=vmail:vmail argv=/usr/bin/spamc -f -e /usr/libexec/dovecot/deliver -f ${sender} -d ${user}@${nexthop}|g' /etc/postfix/master.cf
|
||||
|
||||
elif [ "$OS" = "NAME=\"CloudLinux\"" ];then
|
||||
sed -i 's|^spamassassin.*|spamassassin unix - n n - - pipe flags=DROhu user=vmail:vmail argv=/usr/bin/spamc -f -e /usr/libexec/dovecot/deliver -f ${sender} -d ${user}@${nexthop}|g' /etc/postfix/master.cf
|
||||
|
||||
fi
|
||||
|
||||
|
||||
echo 'Restart and check services are up'
|
||||
systemctl restart dovecot && systemctl restart postfix && systemctl restart spamassassin
|
||||
175
CPScripts/watchdog.sh
Normal file
175
CPScripts/watchdog.sh
Normal file
@@ -0,0 +1,175 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Add any services to be watched by the watchdog to the SERVICE_LIST
|
||||
# Format of the service list: "Display Name" "Service Name" "semicolon delimited list of watchdog arguments"
|
||||
SERVICE_LIST=(
|
||||
"LiteSpeed" "lsws" "lsws;web;litespeed;openlitespeed"
|
||||
"MariaDB" "mariadb" "mariadb;database;mysql"
|
||||
"PowerDNS" "pdns" "powerdns;dns"
|
||||
"Dovecot" "dovecot" "dovecot;imap;pop3"
|
||||
"PostFix" "postfix" "postfix;smtp"
|
||||
"Pure-FTPd" "pure-ftpd" "pureftpd;pure-ftpd;ftp"
|
||||
)
|
||||
|
||||
SERVICE_COUNT=$((${#SERVICE_LIST[@]}/3))
|
||||
|
||||
show_help() {
|
||||
echo -e "\nrun command: \e[31mnohup bash /etc/cyberpanel/watchdog.sh SERVICE_NAME >/dev/null 2>&1 &\e[39m"
|
||||
echo -e "\nreplace \e[31mSERVICE_NAME\e[39m to the service name, acceptable word:"
|
||||
|
||||
for ((x=0; x<SERVICE_COUNT; x++)) ; do
|
||||
IFS=';' read -ra SERVICE_ARGS <<< "${SERVICE_LIST[(x*3)+2]}"
|
||||
echo -e " \e[31m${SERVICE_ARGS[0]}\e[39m"
|
||||
done
|
||||
|
||||
echo -e "\nWatchdog will check service status every 60 seconds and tries to restart if it is not running and also send an email to designated address"
|
||||
echo -e "\nto exit watchdog , run command \e[31mbash /etc/cyberpanel/watchdog.sh kill\e[39m"
|
||||
echo -e "\n\nplease also create \e[31m/etc/cyberpanel/watchdog.flag\e[39m file with following format:"
|
||||
echo -e "TO=address@email.com"
|
||||
echo -e "SENDER=sender name"
|
||||
echo -e "FROM=sender@email.com"
|
||||
echo -e "You may proceed without flag file , but that will make email sending failed."
|
||||
}
|
||||
|
||||
watchdog_check() {
|
||||
for ((x=0; x<SERVICE_COUNT; x++)) ; do
|
||||
DISPLAY_NAME=${SERVICE_LIST[x*3]}
|
||||
SERVICE_NAME=${SERVICE_LIST[(x*3)+1]}
|
||||
IFS=';' read -ra SERVICE_ARGS <<< "${SERVICE_LIST[(x*3)+2]}"
|
||||
SERVICE_ARG=${SERVICE_ARGS[0]}
|
||||
|
||||
echo -e "\nChecking ${DISPLAY_NAME}..."
|
||||
pid=$(ps aux | grep "watchdog ${SERVICE_ARG}" | grep -v grep | awk '{print $2}')
|
||||
if [[ "$pid" == "" ]] ; then
|
||||
echo -e "\nWatchDog for ${DISPLAY_NAME} is gone , restarting..."
|
||||
nohup watchdog ${SERVICE_ARG} > /dev/null 2>&1 &
|
||||
echo -e "\nWatchDog for ${DISPLAY_NAME} has been started..."
|
||||
else
|
||||
echo -e "\nWatchDog for ${DISPLAY_NAME} is running...\n"
|
||||
echo $(ps aux | grep "watchdog ${SERVICE_ARG}" | grep -v grep)
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
check_service() {
|
||||
systemctl status $NAME 2>&1>/dev/null
|
||||
if [[ $? == "0" ]] ; then
|
||||
if [[ $NAME == "mariadb" ]] ; then
|
||||
pid=$(ps aux | grep "/usr/sbin/mysqld" | grep -v grep | awk '{print $2}')
|
||||
if [[ $pid != "" ]] ; then
|
||||
echo "-1000" > /proc/$pid/oom_score_adj
|
||||
fi
|
||||
pid=$(ps aux | grep "/usr/sbin/mysqld" | grep -v grep | awk '{print $2}')
|
||||
if [[ $pid != "" ]] ; then
|
||||
echo "-1000" > /proc/$pid/oom_score_adj
|
||||
fi
|
||||
fi
|
||||
echo "$NAME service is running..."
|
||||
else
|
||||
echo "$NAME is down , try to restart it..."
|
||||
if [[ $NAME == "lsws" ]] ; then
|
||||
pkill lsphp
|
||||
fi
|
||||
if [[ $NAME == "mariadb" ]] ; then
|
||||
pid=$(ps aux | grep "/usr/sbin/mysqld" | grep -v grep | awk '{print $2}')
|
||||
if [[ $pid != "" ]] ; then
|
||||
echo "-1000" > /proc/$pid/oom_score_adj
|
||||
fi
|
||||
pid=$(ps aux | grep "/usr/sbin/mysqld" | grep -v grep | awk '{print $2}')
|
||||
if [[ $pid != "" ]] ; then
|
||||
echo "-1000" > /proc/$pid/oom_score_adj
|
||||
fi
|
||||
fi
|
||||
systemctl stop $NAME
|
||||
systemctl start $NAME
|
||||
if [ -f /etc/cyberpanel/watchdog.flag ] ; then
|
||||
flag="/etc/cyberpanel/watchdog.flag"
|
||||
LINE3=$(awk 'NR==3' $flag)
|
||||
LINE2=$(awk 'NR==2' $flag)
|
||||
LINE1=$(awk 'NR==1' $flag)
|
||||
|
||||
FROM=${LINE3#*=}
|
||||
SENDER=${LINE2#*=}
|
||||
TO=${LINE1#*=}
|
||||
sendmail -F $SENDER -f $FROM -i $TO <<MAIL_END
|
||||
Subject: $NAME is down...
|
||||
To: $TO
|
||||
$NAME is down , watchdog attempted to restarting it...
|
||||
|
||||
MAIL_END
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
if [[ $1 == "help" ]] || [[ $1 == "-h" ]] || [[ $1 == "--help" ]] || [[ $1 == "" ]] ; then
|
||||
show_help
|
||||
exit
|
||||
elif [[ $1 == "check" ]] || [[ $1 == "status" ]] ; then
|
||||
watchdog_check
|
||||
exit
|
||||
elif [[ $1 == "kill" ]] ; then
|
||||
for ((x=0; x<SERVICE_COUNT; x++)); do
|
||||
IFS=';' read -ra SERVICE_ARGS <<< "${SERVICE_LIST[(x*3)+2]}"
|
||||
SERVICE_ARG=${SERVICE_ARGS[0]}
|
||||
|
||||
pid=$(ps aux | grep "watchdog ${SERVICE_ARG}" | grep -v grep | awk '{print $2}')
|
||||
if [[ "$pid" != "" ]] ; then
|
||||
kill -15 $pid
|
||||
fi
|
||||
done
|
||||
echo "watchdog has been killed..."
|
||||
exit
|
||||
fi
|
||||
|
||||
# Check if $1 matches any service argument names
|
||||
SERVICE_FOUND=0
|
||||
for ((x=0; x<SERVICE_COUNT; x++)) ; do
|
||||
DISPLAY_NAME=${SERVICE_LIST[x*3]}
|
||||
SERVICE_NAME=${SERVICE_LIST[(x*3)+1]}
|
||||
IFS=';' read -ra SERVICE_ARGS <<< "${SERVICE_LIST[(x*3)+2]}"
|
||||
SERVICE_ARG=${SERVICE_ARGS[0]}
|
||||
|
||||
for arg in "${SERVICE_ARGS[@]}" ; do
|
||||
if [[ $1 == "$arg" ]] ; then
|
||||
SERVICE_FOUND=1
|
||||
NAME=$SERVICE_NAME
|
||||
echo "Watchdog on ${DISPLAY_NAME} is starting up ..."
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [[ $SERVICE_FOUND == 0 ]] ; then
|
||||
echo -e "unknown service name \e[31m$1\e[39m..."
|
||||
|
||||
show_help
|
||||
exit
|
||||
fi
|
||||
|
||||
|
||||
|
||||
while [ true = true ]
|
||||
do
|
||||
if [[ $NAME == "pdns" ]] ; then
|
||||
if [ -f /home/cyberpanel/powerdns ] ; then
|
||||
check_service
|
||||
fi
|
||||
elif [[ $NAME == "postfix" ]] ; then
|
||||
if [ -f /home/cyberpanel/postfix ] ; then
|
||||
check_service
|
||||
fi
|
||||
elif [[ $name == "pure-ftpd" ]] || [[ $name == "pure-ftpd-mysql" ]] ; then
|
||||
if [ -f /home/cyberpanel/pureftpd ] ; then
|
||||
if [ -f /etc/lsb-release ] ; then
|
||||
NAME="pure-ftpd-mysql"
|
||||
else
|
||||
NAME="pure-ftpd"
|
||||
fi
|
||||
|
||||
check_service
|
||||
fi
|
||||
else
|
||||
check_service
|
||||
fi
|
||||
sleep 60
|
||||
done
|
||||
10
CyberCP/SecurityLevel.py
Normal file
10
CyberCP/SecurityLevel.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class SecurityLevel(Enum):
|
||||
HIGH = 0
|
||||
LOW = 1
|
||||
|
||||
@staticmethod
|
||||
def list():
|
||||
return list(map(lambda s: s.name, SecurityLevel))
|
||||
0
CyberCP/__init__.py
Normal file
0
CyberCP/__init__.py
Normal file
277
CyberCP/secMiddleware.py
Normal file
277
CyberCP/secMiddleware.py
Normal file
@@ -0,0 +1,277 @@
|
||||
# coding=utf-8
|
||||
import os.path
|
||||
|
||||
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
|
||||
from django.shortcuts import HttpResponse, render
|
||||
import json
|
||||
import re
|
||||
from loginSystem.models import Administrator
|
||||
|
||||
|
||||
class secMiddleware:
|
||||
HIGH = 0
|
||||
LOW = 1
|
||||
|
||||
def get_client_ip(request):
|
||||
ip = request.META.get('HTTP_CF_CONNECTING_IP')
|
||||
if ip is None:
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
return ip
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
|
||||
######
|
||||
|
||||
from plogical.processUtilities import ProcessUtilities
|
||||
FinalURL = request.build_absolute_uri().split('?')[0]
|
||||
|
||||
from urllib.parse import urlparse
|
||||
pathActual = urlparse(FinalURL).path
|
||||
|
||||
# Debug logging removed for performance
|
||||
|
||||
# Define webhook pattern for secure matching
|
||||
import re
|
||||
webhook_pattern = re.compile(r'^/websites/[^/]+/(webhook|gitNotify)/?$')
|
||||
|
||||
if pathActual == "/backup/localInitiate" or pathActual == '/' or pathActual == '/verifyLogin' or pathActual == '/logout' or pathActual.startswith('/api')\
|
||||
or webhook_pattern.match(pathActual) or pathActual.startswith('/cloudAPI'):
|
||||
pass
|
||||
else:
|
||||
# Session check logging removed
|
||||
try:
|
||||
val = request.session['userID']
|
||||
except:
|
||||
if bool(request.body):
|
||||
final_dic = {
|
||||
'error_message': "This request need session.",
|
||||
"errorMessage": "This request need session."}
|
||||
final_json = json.dumps(final_dic)
|
||||
return HttpResponse(final_json)
|
||||
else:
|
||||
from django.shortcuts import redirect
|
||||
from loginSystem.views import loadLoginPage
|
||||
return redirect(loadLoginPage)
|
||||
|
||||
# if os.path.exists(ProcessUtilities.debugPath):
|
||||
# logging.writeToFile(f'Final actual URL without QS {FinalURL}')
|
||||
|
||||
# Request method logging removed
|
||||
|
||||
##########################
|
||||
|
||||
try:
|
||||
uID = request.session['userID']
|
||||
admin = Administrator.objects.get(pk=uID)
|
||||
ipAddr = secMiddleware.get_client_ip(request)
|
||||
|
||||
if ipAddr.find('.') > -1:
|
||||
if request.session['ipAddr'] == ipAddr or admin.securityLevel == secMiddleware.LOW:
|
||||
pass
|
||||
else:
|
||||
del request.session['userID']
|
||||
del request.session['ipAddr']
|
||||
logging.writeToFile(secMiddleware.get_client_ip(request))
|
||||
final_dic = {'error_message': "Session reuse detected, IPAddress logged.",
|
||||
"errorMessage": "Session reuse detected, IPAddress logged."}
|
||||
final_json = json.dumps(final_dic)
|
||||
return HttpResponse(final_json)
|
||||
else:
|
||||
ipAddr = ':'.join(secMiddleware.get_client_ip(request).split(':')[:3])
|
||||
if request.session['ipAddr'] == ipAddr or admin.securityLevel == secMiddleware.LOW:
|
||||
pass
|
||||
else:
|
||||
del request.session['userID']
|
||||
del request.session['ipAddr']
|
||||
logging.writeToFile(secMiddleware.get_client_ip(request))
|
||||
final_dic = {'error_message': "Session reuse detected, IPAddress logged.",
|
||||
"errorMessage": "Session reuse detected, IPAddress logged."}
|
||||
final_json = json.dumps(final_dic)
|
||||
return HttpResponse(final_json)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
if bool(request.body):
|
||||
try:
|
||||
|
||||
# Body scanning logging removed
|
||||
|
||||
# Skip validation entirely for webhook endpoints
|
||||
# Webhook URLs are: /websites/<domain>/webhook or /websites/<domain>/gitNotify
|
||||
# Use the same webhook pattern defined above
|
||||
if webhook_pattern.match(pathActual):
|
||||
response = self.get_response(request)
|
||||
return response
|
||||
|
||||
# logging.writeToFile(request.body)
|
||||
try:
|
||||
data = json.loads(request.body)
|
||||
except:
|
||||
data = request.POST
|
||||
|
||||
for key, value in data.items():
|
||||
valueAlreadyChecked = 0
|
||||
|
||||
# Key/value scanning logging removed
|
||||
|
||||
# Skip validation for ports key to allow port ranges with colons
|
||||
# but only for CSF modifyPorts endpoint
|
||||
if key == 'ports' and pathActual == '/firewall/modifyPorts':
|
||||
# Validate that ports only contain numbers, commas, and colons
|
||||
if type(value) == str:
|
||||
import re
|
||||
# Allow only: digits, commas, colons, and whitespace
|
||||
if re.match(r'^[\d,:,\s]+$', value):
|
||||
continue
|
||||
else:
|
||||
logging.writeToFile(f"Invalid port format in CSF configuration: {value}")
|
||||
final_dic = {
|
||||
'error_message': "Invalid port format. Only numbers, commas, and colons are allowed for port ranges.",
|
||||
"errorMessage": "Invalid port format. Only numbers, commas, and colons are allowed for port ranges."}
|
||||
final_json = json.dumps(final_dic)
|
||||
return HttpResponse(final_json)
|
||||
continue
|
||||
elif key == 'ports':
|
||||
# For other endpoints, ports key continues to skip validation
|
||||
continue
|
||||
|
||||
# Allow protocol parameter for CSF modifyPorts endpoint
|
||||
if key == 'protocol' and pathActual == '/firewall/modifyPorts':
|
||||
# Validate protocol values
|
||||
if value in ['TCP_IN', 'TCP_OUT', 'UDP_IN', 'UDP_OUT']:
|
||||
continue
|
||||
else:
|
||||
logging.writeToFile(f"Invalid protocol in CSF configuration: {value}")
|
||||
final_dic = {
|
||||
'error_message': "Invalid protocol. Only TCP_IN, TCP_OUT, UDP_IN, UDP_OUT are allowed.",
|
||||
"errorMessage": "Invalid protocol. Only TCP_IN, TCP_OUT, UDP_IN, UDP_OUT are allowed."}
|
||||
final_json = json.dumps(final_dic)
|
||||
return HttpResponse(final_json)
|
||||
|
||||
if type(value) == str or type(value) == bytes:
|
||||
pass
|
||||
elif type(value) == list:
|
||||
valueAlreadyChecked = 1
|
||||
# List type logging removed
|
||||
for items in value:
|
||||
if isinstance(items, str) and (items.find('- -') > -1 or items.find('\n') > -1 or items.find(';') > -1 or items.find(
|
||||
'&&') > -1 or items.find('|') > -1 or items.find('...') > -1 \
|
||||
or items.find("`") > -1 or items.find("$") > -1 or items.find(
|
||||
"(") > -1 or items.find(")") > -1 \
|
||||
or items.find("'") > -1 or items.find("[") > -1 or items.find(
|
||||
"]") > -1 or items.find("{") > -1 or items.find("}") > -1 \
|
||||
or items.find(":") > -1 or items.find("<") > -1 or items.find(
|
||||
">") > -1 or items.find("&") > -1):
|
||||
logging.writeToFile(request.body)
|
||||
final_dic = {
|
||||
'error_message': "Data supplied is not accepted, following characters are not allowed in the input ` $ & ( ) [ ] { } ; : ‘ < >.",
|
||||
"errorMessage": "Data supplied is not accepted, following characters are not allowed in the input ` $ & ( ) [ ] { } ; : ‘ < >."}
|
||||
final_json = json.dumps(final_dic)
|
||||
return HttpResponse(final_json)
|
||||
else:
|
||||
continue
|
||||
|
||||
if key == 'backupDestinations':
|
||||
if re.match('^[a-z|0-9]+:[a-z|0-9|\.]+\/?[A-Z|a-z|0-9|\.]*$',
|
||||
value) == None and value != 'local':
|
||||
logging.writeToFile(request.body)
|
||||
final_dic = {'error_message': "Data supplied is not accepted.",
|
||||
"errorMessage": "Data supplied is not accepted."}
|
||||
final_json = json.dumps(final_dic)
|
||||
return HttpResponse(final_json)
|
||||
|
||||
# Allow JSON structure characters for API endpoints but keep security checks for dangerous characters
|
||||
isAPIEndpoint = (pathActual.find('api/remoteTransfer') > -1 or pathActual.find('api/verifyConn') > -1 or
|
||||
pathActual.find('saveSpamAssassinConfigurations') > -1 or
|
||||
pathActual.find('docker') > -1 or pathActual.find('cloudAPI') > -1 or
|
||||
pathActual.find('verifyLogin') > -1 or pathActual.find('submitUserCreation') > -1 or
|
||||
pathActual.find('/api/') > -1 or pathActual.find('aiscanner/scheduled-scans') > -1)
|
||||
|
||||
if isAPIEndpoint:
|
||||
# For API endpoints, still check for the most dangerous command injection characters
|
||||
if isinstance(value, (str, bytes)) and (value.find('- -') > -1 or value.find('\n') > -1 or value.find(';') > -1 or
|
||||
value.find('&&') > -1 or value.find('||') > -1 or value.find('|') > -1 or
|
||||
value.find('...') > -1 or value.find("`") > -1 or value.find("$") > -1 or
|
||||
value.find('../') > -1 or value.find('../../') > -1):
|
||||
logging.writeToFile(request.body)
|
||||
final_dic = {
|
||||
'error_message': "API request contains potentially dangerous characters: `;`, `&&`, `||`, `|`, `` ` ``, `$`, `../` are not allowed.",
|
||||
"errorMessage": "API request contains potentially dangerous characters."
|
||||
}
|
||||
final_json = json.dumps(final_dic)
|
||||
return HttpResponse(final_json)
|
||||
continue
|
||||
if key == 'MainDashboardCSS' or key == 'ownerPassword' or key == 'scriptUrl' or key == 'CLAMAV_VIRUS' or key == "Rspamdserver" or key == 'smtpd_milters' \
|
||||
or key == 'non_smtpd_milters' or key == 'key' or key == 'cert' or key == 'recordContentAAAA' or key == 'backupDestinations'\
|
||||
or key == 'ports' \
|
||||
or key == 'imageByPass' or key == 'passwordByPass' or key == 'PasswordByPass' or key == 'cronCommand' \
|
||||
or key == 'emailMessage' or key == 'configData' or key == 'rewriteRules' \
|
||||
or key == 'modSecRules' or key == 'recordContentTXT' or key == 'SecAuditLogRelevantStatus' \
|
||||
or key == 'fileContent' or key == 'commands' or key == 'gitHost' or key == 'ipv6' or key == 'contentNow' \
|
||||
or key == 'time_of_day' or key == 'notification_emails' or key == 'domains':
|
||||
continue
|
||||
|
||||
# Skip validation for API endpoints that need JSON structure characters
|
||||
if not isAPIEndpoint and valueAlreadyChecked == 0:
|
||||
# Only check string values, skip lists and other types
|
||||
if (type(value) == str or type(value) == bytes) and (value.find('- -') > -1 or value.find('\n') > -1 or value.find(';') > -1 or value.find(
|
||||
'&&') > -1 or value.find('|') > -1 or value.find('...') > -1 \
|
||||
or value.find("`") > -1 or value.find("$") > -1 or value.find("(") > -1 or value.find(
|
||||
")") > -1 \
|
||||
or value.find("'") > -1 or value.find("[") > -1 or value.find("]") > -1 or value.find(
|
||||
"{") > -1 or value.find("}") > -1 \
|
||||
or value.find(":") > -1 or value.find("<") > -1 or value.find(">") > -1 or value.find(
|
||||
"&") > -1):
|
||||
logging.writeToFile(request.body)
|
||||
final_dic = {
|
||||
'error_message': "Data supplied is not accepted, following characters are not allowed in the input ` $ & ( ) [ ] { } ; : ‘ < >.",
|
||||
"errorMessage": "Data supplied is not accepted, following characters are not allowed in the input ` $ & ( ) [ ] { } ; : ‘ < >."}
|
||||
final_json = json.dumps(final_dic)
|
||||
return HttpResponse(final_json)
|
||||
# Skip key validation for API endpoints that need JSON structure characters
|
||||
if not isAPIEndpoint and (key.find(';') > -1 or key.find('&&') > -1 or key.find('|') > -1 or key.find('...') > -1 \
|
||||
or key.find("`") > -1 or key.find("$") > -1 or key.find("(") > -1 or key.find(")") > -1 \
|
||||
or key.find("'") > -1 or key.find("[") > -1 or key.find("]") > -1 or key.find(
|
||||
"{") > -1 or key.find("}") > -1 \
|
||||
or key.find(":") > -1 or key.find("<") > -1 or key.find(">") > -1 or key.find("&") > -1):
|
||||
logging.writeToFile(request.body)
|
||||
final_dic = {'error_message': "Data supplied is not accepted.",
|
||||
"errorMessage": "Data supplied is not accepted following characters are not allowed in the input ` $ & ( ) [ ] { } ; : ‘ < >."}
|
||||
final_json = json.dumps(final_dic)
|
||||
return HttpResponse(final_json)
|
||||
|
||||
except BaseException as msg:
|
||||
final_dic = {'error_message': f"Error: {str(msg)}",
|
||||
"errorMessage": f"Error: {str(msg)}"}
|
||||
final_json = json.dumps(final_dic)
|
||||
return HttpResponse(final_json)
|
||||
else:
|
||||
# No body logging removed
|
||||
pass
|
||||
# else:
|
||||
# try:
|
||||
# if request.path.find('cloudAPI/') > -1 or request.path.find('api/') > -1:
|
||||
# pass
|
||||
# else:
|
||||
# uID = request.session['userID']
|
||||
# except:
|
||||
# return render(request, 'loginSystem/login.html', {})
|
||||
|
||||
response = self.get_response(request)
|
||||
|
||||
response['X-XSS-Protection'] = "1; mode=block"
|
||||
response['X-Frame-Options'] = "sameorigin"
|
||||
response['Content-Security-Policy'] = "script-src 'self' https://www.jsdelivr.com"
|
||||
response['Content-Security-Policy'] = "connect-src *;"
|
||||
response['Content-Security-Policy'] = "font-src 'self' 'unsafe-inline' https://www.jsdelivr.com https://fonts.googleapis.com"
|
||||
response[
|
||||
'Content-Security-Policy'] = "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://www.jsdelivr.com https://cdnjs.cloudflare.com https://maxcdn.bootstrapcdn.com https://cdn.jsdelivr.net"
|
||||
# response['Content-Security-Policy'] = "default-src 'self' cyberpanel.cloud *.cyberpanel.cloud"
|
||||
response['X-Content-Type-Options'] = "nosniff"
|
||||
response['Referrer-Policy'] = "same-origin"
|
||||
|
||||
return response
|
||||
197
CyberCP/settings.py
Normal file
197
CyberCP/settings.py
Normal file
@@ -0,0 +1,197 @@
|
||||
"""
|
||||
Django settings for CyberCP project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 1.11.3.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.11/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/1.11/ref/settings/
|
||||
"""
|
||||
|
||||
import os
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'xr%j*p!*$0d%(-(e%@-*hyoz4$f%y77coq0u)6pwmjg4)q&19f'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = False
|
||||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'baseTemplate',
|
||||
'firewall',
|
||||
'loginSystem',
|
||||
'packages',
|
||||
'websiteFunctions',
|
||||
'tuning',
|
||||
'serverStatus',
|
||||
'dns',
|
||||
'ftp',
|
||||
'userManagment',
|
||||
'databases',
|
||||
'mailServer',
|
||||
'serverLogs',
|
||||
'backup',
|
||||
'managePHP',
|
||||
'manageSSL',
|
||||
'api',
|
||||
'filemanager',
|
||||
'manageServices',
|
||||
'pluginHolder',
|
||||
'emailPremium',
|
||||
'emailMarketing',
|
||||
'cloudAPI',
|
||||
'highAvailability',
|
||||
's3Backups',
|
||||
'dockerManager',
|
||||
'containerization',
|
||||
'CLManager',
|
||||
'IncBackups',
|
||||
'aiScanner',
|
||||
# 'WebTerminal'
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'CyberCP.secMiddleware.secMiddleware'
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'CyberCP.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [os.path.join(BASE_DIR, 'templates')]
|
||||
,
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
'baseTemplate.context_processors.version_context',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'CyberCP.wsgi.application'
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
|
||||
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': 'cyberpanel',
|
||||
'USER': 'cyberpanel',
|
||||
'PASSWORD': 'SLTUIUxqhulwsh',
|
||||
'HOST': 'localhost',
|
||||
'PORT':''
|
||||
},
|
||||
'rootdb': {
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': 'mysql',
|
||||
'USER': 'root',
|
||||
'PASSWORD': 'SLTUIUxqhulwsh',
|
||||
'HOST': 'localhost',
|
||||
'PORT': '',
|
||||
},
|
||||
}
|
||||
DATABASE_ROUTERS = ['backup.backupRouter.backupRouter']
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.11/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.11/howto/static-files/
|
||||
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
LOCALE_PATHS = (
|
||||
os.path.join(BASE_DIR, 'locale'),
|
||||
)
|
||||
|
||||
LANGUAGES = (
|
||||
('en', _('English')),
|
||||
('cn', _('Chinese')),
|
||||
('br', _('Bulgarian')),
|
||||
('pt', _('Portuguese')),
|
||||
('ja', _('Japanese')),
|
||||
('bs', _('Bosnian')),
|
||||
('gr', _('Greek')),
|
||||
('ru', _('Russian')),
|
||||
('tr', _('Turkish')),
|
||||
('es', _('Spanish')),
|
||||
('fr', _('French')),
|
||||
('pl', _('Polish')),
|
||||
('vi', _('Vietnamese')),
|
||||
('it', _('Italian')),
|
||||
('de', _('Deutsch')),
|
||||
('id', _('Indonesian')),
|
||||
('bn', _('Bangla')),
|
||||
)
|
||||
|
||||
MEDIA_URL = '/usr/local/CyberCP/tmp/'
|
||||
MEDIA_ROOT = MEDIA_URL
|
||||
DATA_UPLOAD_MAX_MEMORY_SIZE = 2147483648
|
||||
|
||||
# Security settings for CSF compliance
|
||||
X_FRAME_OPTIONS = 'SAMEORIGIN'
|
||||
49
CyberCP/urls.py
Normal file
49
CyberCP/urls.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""CyberCP URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/1.11/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import path, include
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.urls import path, re_path, include
|
||||
from django.contrib import admin
|
||||
|
||||
urlpatterns = [
|
||||
path('base/', include('baseTemplate.urls')),
|
||||
path('', include('loginSystem.urls')),
|
||||
path('packages/', include('packages.urls')),
|
||||
path('websites/', include('websiteFunctions.urls')),
|
||||
path('tuning/', include('tuning.urls')),
|
||||
path('ftp/', include('ftp.urls')),
|
||||
path('serverstatus/', include('serverStatus.urls')),
|
||||
path('dns/', include('dns.urls')),
|
||||
path('users/', include('userManagment.urls')),
|
||||
path('dataBases/', include('databases.urls')),
|
||||
path('email/', include('mailServer.urls')),
|
||||
path('serverlogs/', include('serverLogs.urls')),
|
||||
path('firewall/', include('firewall.urls')),
|
||||
path('backup/', include('backup.urls')),
|
||||
path('managephp/', include('managePHP.urls')),
|
||||
path('manageSSL/', include('manageSSL.urls')),
|
||||
path('api/', include('api.urls')),
|
||||
path('filemanager/', include('filemanager.urls')),
|
||||
path('emailPremium/', include('emailPremium.urls')),
|
||||
path('manageservices/', include('manageServices.urls')),
|
||||
path('plugins/', include('pluginHolder.urls')),
|
||||
path('emailMarketing/', include('emailMarketing.urls')),
|
||||
path('cloudAPI/', include('cloudAPI.urls')),
|
||||
path('docker/', include('dockerManager.urls')),
|
||||
path('container/', include('containerization.urls')),
|
||||
path('CloudLinux/', include('CLManager.urls')),
|
||||
path('IncrementalBackups/', include('IncBackups.urls')),
|
||||
path('aiscanner/', include('aiScanner.urls')),
|
||||
# path('Terminal/', include('WebTerminal.urls')),
|
||||
]
|
||||
17
CyberCP/wsgi.py
Normal file
17
CyberCP/wsgi.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""
|
||||
WSGI config for CyberCP project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
|
||||
|
||||
application = get_wsgi_application()
|
||||
2
FetchIP.sh
Normal file
2
FetchIP.sh
Normal file
@@ -0,0 +1,2 @@
|
||||
Server_IP=$(curl --silent --max-time 30 -4 https://cyberpanel.sh/?ip)
|
||||
echo "$Server_IP" > "/etc/cyberpanel/machineIP"
|
||||
BIN
IncBackups/.DS_Store
vendored
Normal file
BIN
IncBackups/.DS_Store
vendored
Normal file
Binary file not shown.
7
IncBackups/IncBackupPath.py
Normal file
7
IncBackups/IncBackupPath.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class IncBackupPath(Enum):
|
||||
SFTP = "/home/cyberpanel/sftp"
|
||||
AWS = "/home/cyberpanel/aws"
|
||||
# WASABI = "/home/cyberpanel/wasabi"
|
||||
8
IncBackups/IncBackupProvider.py
Normal file
8
IncBackups/IncBackupProvider.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from enum import Enum, auto
|
||||
|
||||
|
||||
class IncBackupProvider(Enum):
|
||||
LOCAL = auto()
|
||||
SFTP = auto()
|
||||
AWS = auto()
|
||||
# WASABI = auto()
|
||||
394
IncBackups/IncBackups.py
Normal file
394
IncBackups/IncBackups.py
Normal file
@@ -0,0 +1,394 @@
|
||||
#!/usr/local/CyberCP/bin/python
|
||||
import os,sys
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
import django
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
|
||||
django.setup()
|
||||
import threading as multi
|
||||
from plogical.processUtilities import ProcessUtilities
|
||||
import time
|
||||
from .models import IncJob, JobSnapshots
|
||||
from websiteFunctions.models import Websites
|
||||
import plogical.randomPassword as randomPassword
|
||||
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
|
||||
from xml.etree.ElementTree import Element, SubElement
|
||||
from xml.etree import ElementTree
|
||||
from xml.dom import minidom
|
||||
from backup.models import DBUsers
|
||||
import plogical.mysqlUtilities as mysqlUtilities
|
||||
from plogical.backupUtilities import backupUtilities
|
||||
from plogical.dnsUtilities import DNS
|
||||
from mailServer.models import Domains as eDomains
|
||||
from random import randint
|
||||
|
||||
|
||||
class IncJobs(multi.Thread):
|
||||
|
||||
def __init__(self, function, extraArgs):
|
||||
multi.Thread.__init__(self)
|
||||
self.function = function
|
||||
self.extraArgs = extraArgs
|
||||
self.repoPath = ''
|
||||
self.passwordFile = ''
|
||||
self.statusPath = ''
|
||||
self.website = ''
|
||||
self.backupDestinations = ''
|
||||
self.jobid = 0
|
||||
|
||||
def run(self):
|
||||
|
||||
if self.function == 'createBackup':
|
||||
self.createBackup()
|
||||
|
||||
def prepareBackupMeta(self):
|
||||
try:
|
||||
|
||||
######### Generating meta
|
||||
|
||||
## XML Generation
|
||||
|
||||
metaFileXML = Element('metaFile')
|
||||
|
||||
child = SubElement(metaFileXML, 'masterDomain')
|
||||
child.text = self.website.domain
|
||||
|
||||
child = SubElement(metaFileXML, 'phpSelection')
|
||||
child.text = self.website.phpSelection
|
||||
|
||||
child = SubElement(metaFileXML, 'externalApp')
|
||||
child.text = self.website.externalApp
|
||||
|
||||
childDomains = self.website.childdomains_set.all()
|
||||
|
||||
databases = self.website.databases_set.all()
|
||||
|
||||
## Child domains XML
|
||||
|
||||
childDomainsXML = Element('ChildDomains')
|
||||
|
||||
for items in childDomains:
|
||||
childDomainXML = Element('domain')
|
||||
|
||||
child = SubElement(childDomainXML, 'domain')
|
||||
child.text = items.domain
|
||||
child = SubElement(childDomainXML, 'phpSelection')
|
||||
child.text = items.phpSelection
|
||||
child = SubElement(childDomainXML, 'path')
|
||||
child.text = items.path
|
||||
|
||||
childDomainsXML.append(childDomainXML)
|
||||
|
||||
metaFileXML.append(childDomainsXML)
|
||||
|
||||
## Databases XML
|
||||
|
||||
databasesXML = Element('Databases')
|
||||
|
||||
for items in databases:
|
||||
try:
|
||||
dbuser = DBUsers.objects.get(user=items.dbUser)
|
||||
userToTry = items.dbUser
|
||||
except:
|
||||
dbusers = DBUsers.objects.all().filter(user=items.dbUser)
|
||||
userToTry = items.dbUser
|
||||
for it in dbusers:
|
||||
dbuser = it
|
||||
break
|
||||
|
||||
userToTry = mysqlUtilities.mysqlUtilities.fetchuser(items.dbName)
|
||||
|
||||
try:
|
||||
dbuser = DBUsers.objects.get(user=userToTry)
|
||||
except:
|
||||
dbusers = DBUsers.objects.all().filter(user=userToTry)
|
||||
for it in dbusers:
|
||||
dbuser = it
|
||||
break
|
||||
|
||||
databaseXML = Element('database')
|
||||
|
||||
child = SubElement(databaseXML, 'dbName')
|
||||
child.text = items.dbName
|
||||
child = SubElement(databaseXML, 'dbUser')
|
||||
child.text = userToTry
|
||||
child = SubElement(databaseXML, 'password')
|
||||
child.text = dbuser.password
|
||||
|
||||
databasesXML.append(databaseXML)
|
||||
|
||||
metaFileXML.append(databasesXML)
|
||||
|
||||
## Get Aliases
|
||||
|
||||
aliasesXML = Element('Aliases')
|
||||
|
||||
aliases = backupUtilities.getAliases(self.website.domain)
|
||||
|
||||
for items in aliases:
|
||||
child = SubElement(aliasesXML, 'alias')
|
||||
child.text = items
|
||||
|
||||
metaFileXML.append(aliasesXML)
|
||||
|
||||
## Finish Alias
|
||||
|
||||
## DNS Records XML
|
||||
|
||||
try:
|
||||
|
||||
dnsRecordsXML = Element("dnsrecords")
|
||||
dnsRecords = DNS.getDNSRecords(self.website.domain)
|
||||
|
||||
for items in dnsRecords:
|
||||
dnsRecordXML = Element('dnsrecord')
|
||||
|
||||
child = SubElement(dnsRecordXML, 'type')
|
||||
child.text = items.type
|
||||
child = SubElement(dnsRecordXML, 'name')
|
||||
child.text = items.name
|
||||
child = SubElement(dnsRecordXML, 'content')
|
||||
child.text = items.content
|
||||
child = SubElement(dnsRecordXML, 'priority')
|
||||
child.text = str(items.prio)
|
||||
|
||||
dnsRecordsXML.append(dnsRecordXML)
|
||||
|
||||
metaFileXML.append(dnsRecordsXML)
|
||||
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath, '%s. [158:prepMeta]' % (str(msg)), 1)
|
||||
|
||||
## Email accounts XML
|
||||
|
||||
try:
|
||||
emailRecordsXML = Element('emails')
|
||||
eDomain = eDomains.objects.get(domain=self.website.domain)
|
||||
emailAccounts = eDomain.eusers_set.all()
|
||||
|
||||
for items in emailAccounts:
|
||||
emailRecordXML = Element('emailAccount')
|
||||
|
||||
child = SubElement(emailRecordXML, 'email')
|
||||
child.text = items.email
|
||||
child = SubElement(emailRecordXML, 'password')
|
||||
child.text = items.password
|
||||
|
||||
emailRecordsXML.append(emailRecordXML)
|
||||
|
||||
metaFileXML.append(emailRecordsXML)
|
||||
except BaseException as msg:
|
||||
logging.writeToFile(self.statusPath, '%s. [warning:179:prepMeta]' % (str(msg)), 1)
|
||||
|
||||
## Email meta generated!
|
||||
|
||||
def prettify(elem):
|
||||
"""Return a pretty-printed XML string for the Element.
|
||||
"""
|
||||
rough_string = ElementTree.tostring(elem, 'utf-8')
|
||||
reparsed = minidom.parseString(rough_string)
|
||||
return reparsed.toprettyxml(indent=" ")
|
||||
|
||||
## /home/example.com/backup/backup-example-06-50-03-Thu-Feb-2018/meta.xml -- metaPath
|
||||
|
||||
metaPath = '/home/cyberpanel/%s' % (str(randint(1000, 9999)))
|
||||
|
||||
xmlpretty = prettify(metaFileXML).encode('ascii', 'ignore')
|
||||
metaFile = open(metaPath, 'w')
|
||||
metaFile.write(xmlpretty)
|
||||
metaFile.close()
|
||||
os.chmod(metaPath, 0o640)
|
||||
|
||||
## meta generated
|
||||
|
||||
logging.statusWriter(self.statusPath, 'Meta data is ready..', 1)
|
||||
|
||||
metaPathNew = '/home/%s/meta.xml' % (self.website.domain)
|
||||
command = 'mv %s %s' % (metaPath, metaPathNew)
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
command = 'chown %s:%s %s' % (self.website.externalApp, self.website.externalApp, metaPathNew)
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
return 1
|
||||
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath, "%s [207][5009]" % (str(msg)), 1)
|
||||
return 0
|
||||
|
||||
def backupData(self):
|
||||
try:
|
||||
logging.statusWriter(self.statusPath, 'Backing up data..', 1)
|
||||
|
||||
backupPath = '/home/%s' % (self.website.domain)
|
||||
# Define our excludes file for use with restic
|
||||
backupExcludesFile = '/home/%s/backup-exclude.conf' % (self.website.domain)
|
||||
resticBackupExcludeCMD = ' --exclude-file=%s' % (backupExcludesFile)
|
||||
|
||||
if self.backupDestinations == 'local':
|
||||
command = 'restic -r %s backup %s --password-file %s --exclude %s' % (self.repoPath, backupPath, self.passwordFile, self.repoPath)
|
||||
# If /home/%s/backup-exclude.conf file exists lets pass this to restic by appending the command to end.
|
||||
if os.path.isfile(backupExcludesFile):
|
||||
command = command + resticBackupExcludeCMD
|
||||
snapShotid = ProcessUtilities.outputExecutioner(command).split(' ')[-2]
|
||||
|
||||
newSnapshot = JobSnapshots(job=self.jobid, type='data:%s' % (backupPath), snapshotid=snapShotid, destination=self.backupDestinations)
|
||||
newSnapshot.save()
|
||||
|
||||
|
||||
elif self.backupDestinations[:4] == 'sftp':
|
||||
remotePath = '/home/backup/%s' % (self.website.domain)
|
||||
command = 'export PATH=${PATH}:/usr/bin && restic -r %s:%s backup %s --password-file %s --exclude %s' % (self.backupDestinations, remotePath, backupPath, self.passwordFile, self.repoPath)
|
||||
# If /home/%s/backup-exclude.conf file exists lets pass this to restic by appending the command to end.
|
||||
if os.path.isfile(backupExcludesFile):
|
||||
command = command + resticBackupExcludeCMD
|
||||
snapShotid = ProcessUtilities.outputExecutioner(command).split(' ')[-2]
|
||||
newSnapshot = JobSnapshots(job=self.jobid, type='data:%s' % (remotePath), snapshotid=snapShotid,
|
||||
destination=self.backupDestinations)
|
||||
newSnapshot.save()
|
||||
|
||||
logging.statusWriter(self.statusPath, 'Data for %s backed to %s.' % (self.website.domain, self.backupDestinations), 1)
|
||||
return 1
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath,'%s. [IncJobs.backupData.223][5009]' % str(msg), 1)
|
||||
return 0
|
||||
|
||||
def backupDatabases(self):
|
||||
try:
|
||||
logging.statusWriter(self.statusPath, 'Backing up databases..', 1)
|
||||
|
||||
databases = self.website.databases_set.all()
|
||||
|
||||
for items in databases:
|
||||
if mysqlUtilities.mysqlUtilities.createDatabaseBackup(items.dbName, '/home/cyberpanel') == 0:
|
||||
return 0
|
||||
|
||||
dbPath = '/home/cyberpanel/%s.sql' % (items.dbName)
|
||||
|
||||
if self.backupDestinations == 'local':
|
||||
command = 'restic -r %s backup %s --password-file %s' % (self.repoPath, dbPath, self.passwordFile)
|
||||
snapShotid = ProcessUtilities.outputExecutioner(command).split(' ')[-2]
|
||||
|
||||
newSnapshot = JobSnapshots(job=self.jobid, type='database:%s' % (items.dbName), snapshotid=snapShotid, destination=self.backupDestinations)
|
||||
newSnapshot.save()
|
||||
|
||||
elif self.backupDestinations[:4] == 'sftp':
|
||||
remotePath = '/home/backup/%s' % (self.website.domain)
|
||||
command = 'export PATH=${PATH}:/usr/bin && restic -r %s:%s backup %s --password-file %s --exclude %s' % (
|
||||
self.backupDestinations, remotePath, dbPath, self.passwordFile, self.repoPath)
|
||||
snapShotid = ProcessUtilities.outputExecutioner(command).split(' ')[-2]
|
||||
newSnapshot = JobSnapshots(job=self.jobid, type='database:%s' % (items.dbName), snapshotid=snapShotid,
|
||||
destination=self.backupDestinations)
|
||||
newSnapshot.save()
|
||||
return 1
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath,'%s. [IncJobs.backupDatabases.269][5009]' % str(msg), 1)
|
||||
return 0
|
||||
|
||||
def emailBackup(self):
|
||||
try:
|
||||
logging.statusWriter(self.statusPath, 'Backing up emails..', 1)
|
||||
|
||||
backupPath = '/home/vmail/%s' % (self.website.domain)
|
||||
|
||||
if os.path.exists(backupPath):
|
||||
if self.backupDestinations == 'local':
|
||||
logging.statusWriter(self.statusPath, 'hello world', 1)
|
||||
command = 'restic -r %s backup %s --password-file %s' % (
|
||||
self.repoPath, backupPath, self.passwordFile)
|
||||
snapShotid = ProcessUtilities.outputExecutioner(command).split(' ')[-2]
|
||||
|
||||
newSnapshot = JobSnapshots(job=self.jobid, type='email:%s' % (backupPath), snapshotid=snapShotid,
|
||||
destination=self.backupDestinations)
|
||||
newSnapshot.save()
|
||||
logging.statusWriter(self.statusPath, 'hello world 2', 1)
|
||||
|
||||
elif self.backupDestinations[:4] == 'sftp':
|
||||
remotePath = '/home/backup/%s' % (self.website.domain)
|
||||
command = 'export PATH=${PATH}:/usr/bin && restic -r %s:%s backup %s --password-file %s --exclude %s' % (
|
||||
self.backupDestinations, remotePath, backupPath, self.passwordFile, self.repoPath)
|
||||
snapShotid = ProcessUtilities.outputExecutioner(command).split(' ')[-2]
|
||||
newSnapshot = JobSnapshots(job=self.jobid, type='email:%s' % (backupPath), snapshotid=snapShotid,
|
||||
destination=self.backupDestinations)
|
||||
newSnapshot.save()
|
||||
|
||||
logging.statusWriter(self.statusPath, 'Emails for %s backed to %s.' % (self.website.domain, self.backupDestinations), 1)
|
||||
return 1
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath,'%s. [IncJobs.backupDatabases.269][5009]' % str(msg), 1)
|
||||
return 0
|
||||
|
||||
def initiateRepo(self):
|
||||
try:
|
||||
logging.statusWriter(self.statusPath, 'Will first initiate backup repo..', 1)
|
||||
|
||||
# Define our excludes file for use with restic
|
||||
backupExcludesFile = '/home/%s/backup-exclude.conf' % (self.website.domain)
|
||||
resticBackupExcludeCMD = ' --exclude-file=%s' % (backupExcludesFile)
|
||||
|
||||
if self.backupDestinations == 'local':
|
||||
command = 'restic init --repo %s --password-file %s' % (self.repoPath, self.passwordFile)
|
||||
# If /home/%s/backup-exclude.conf file exists lets pass this to restic by appending the command to end.
|
||||
if os.path.isfile(backupExcludesFile):
|
||||
command = command + resticBackupExcludeCMD
|
||||
ProcessUtilities.executioner(command, self.website.externalApp)
|
||||
|
||||
elif self.backupDestinations[:4] == 'sftp':
|
||||
remotePath = '/home/backup/%s' % (self.website.domain)
|
||||
command = 'export PATH=${PATH}:/usr/bin && restic init --repo %s:%s --password-file %s' % (self.backupDestinations, remotePath, self.passwordFile)
|
||||
# If /home/%s/backup-exclude.conf file exists lets pass this to restic by appending the command to end.
|
||||
if os.path.isfile(backupExcludesFile):
|
||||
command = command + resticBackupExcludeCMD
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
logging.statusWriter(self.statusPath, 'Repo %s initiated for %s.' % (self.backupDestinations, self.website.domain), 1)
|
||||
return 1
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath,'%s. [IncJobs.initiateRepo.47][5009]' % str(msg), 1)
|
||||
return 0
|
||||
|
||||
def createBackup(self):
|
||||
self.statusPath = self.extraArgs['tempPath']
|
||||
website = self.extraArgs['website']
|
||||
self.backupDestinations = self.extraArgs['backupDestinations']
|
||||
websiteData = self.extraArgs['websiteData']
|
||||
websiteEmails = self.extraArgs['websiteEmails']
|
||||
websiteSSLs = self.extraArgs['websiteSSLs']
|
||||
websiteDatabases = self.extraArgs['websiteDatabases']
|
||||
|
||||
self.website = Websites.objects.get(domain=website)
|
||||
|
||||
newJob = IncJob(website=self.website)
|
||||
newJob.save()
|
||||
|
||||
self.jobid = newJob
|
||||
|
||||
self.passwordFile = '/home/%s/%s' % (self.website.domain, self.website.domain)
|
||||
password = randomPassword.generate_pass()
|
||||
|
||||
self.repoPath = '/home/%s/incbackup' % (self.website.domain)
|
||||
|
||||
if not os.path.exists(self.passwordFile):
|
||||
command = 'echo "%s" > %s' % (password, self.passwordFile)
|
||||
ProcessUtilities.executioner(command, self.website.externalApp)
|
||||
|
||||
|
||||
if self.initiateRepo() == 0:
|
||||
return
|
||||
|
||||
if self.prepareBackupMeta() == 0:
|
||||
return
|
||||
|
||||
if websiteData:
|
||||
if self.backupData() == 0:
|
||||
return
|
||||
|
||||
if websiteDatabases:
|
||||
if self.backupDatabases() == 0:
|
||||
return
|
||||
|
||||
|
||||
if websiteEmails:
|
||||
if self.emailBackup() == 0:
|
||||
return
|
||||
|
||||
logging.statusWriter(self.statusPath, 'Completed', 1)
|
||||
950
IncBackups/IncBackupsControl.py
Normal file
950
IncBackups/IncBackupsControl.py
Normal file
@@ -0,0 +1,950 @@
|
||||
#!/usr/local/CyberCP/bin/python
|
||||
import os
|
||||
import os.path
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import requests
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
|
||||
|
||||
import django
|
||||
|
||||
try:
|
||||
django.setup()
|
||||
except:
|
||||
pass
|
||||
import threading as multi
|
||||
from plogical.processUtilities import ProcessUtilities
|
||||
from .models import IncJob, JobSnapshots
|
||||
from websiteFunctions.models import Websites
|
||||
import plogical.randomPassword as randomPassword
|
||||
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
|
||||
import plogical.mysqlUtilities as mysqlUtilities
|
||||
import json
|
||||
from django.shortcuts import HttpResponse
|
||||
|
||||
try:
|
||||
from plogical.virtualHostUtilities import virtualHostUtilities
|
||||
from plogical.mailUtilities import mailUtilities
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
class IncJobs(multi.Thread):
|
||||
|
||||
def __init__(self, function, extraArgs):
|
||||
multi.Thread.__init__(self)
|
||||
self.function = function
|
||||
self.extraArgs = extraArgs
|
||||
self.repoPath = ''
|
||||
self.passwordFile = ''
|
||||
self.statusPath = ''
|
||||
self.website = ''
|
||||
self.backupDestinations = ''
|
||||
self.jobid = 0
|
||||
self.metaPath = ''
|
||||
self.path = ''
|
||||
self.reconstruct = ''
|
||||
|
||||
def run(self):
|
||||
|
||||
if self.function == 'createBackup':
|
||||
self.createBackup()
|
||||
elif self.function == 'restorePoint':
|
||||
self.restorePoint()
|
||||
elif self.function == 'remoteRestore':
|
||||
self.restorePoint()
|
||||
|
||||
def getRemoteBackups(self):
|
||||
if self.backupDestinations[:4] == 'sftp':
|
||||
path = '/home/backup/%s' % (self.website)
|
||||
command = 'export RESTIC_PASSWORD=%s PATH=${PATH}:/usr/bin && restic -r %s:%s snapshots' % (
|
||||
self.passwordFile, self.backupDestinations, path)
|
||||
return ProcessUtilities.outputExecutioner(command, self.externalApp).split('\n')
|
||||
else:
|
||||
key, secret = self.getAWSData()
|
||||
command = 'export RESTIC_PASSWORD=%s AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s && restic -r s3:s3.amazonaws.com/%s snapshots' % (
|
||||
self.passwordFile, key, secret, self.website)
|
||||
return ProcessUtilities.outputExecutioner(command, self.externalApp).split('\n')
|
||||
|
||||
def fetchCurrentBackups(self):
|
||||
try:
|
||||
self.website = self.extraArgs['website']
|
||||
self.backupDestinations = self.extraArgs['backupDestinations']
|
||||
self.passwordFile = self.extraArgs['password']
|
||||
|
||||
result = self.getRemoteBackups()
|
||||
|
||||
activator = 0
|
||||
json_data = []
|
||||
if result[0].find('unable to open config file') == -1:
|
||||
for items in reversed(result):
|
||||
|
||||
if items.find('---------------') > -1:
|
||||
if activator == 0:
|
||||
activator = 1
|
||||
continue
|
||||
else:
|
||||
activator = 0
|
||||
|
||||
if activator:
|
||||
entry = items.split(' ')
|
||||
json_data.append({'id': entry[0],
|
||||
'date': "%s %s" % (entry[2], entry[3]),
|
||||
'host': entry[5],
|
||||
'path': entry[-1]
|
||||
})
|
||||
final_json = json.dumps({'status': 1, 'error_message': "None", "data": json_data})
|
||||
return HttpResponse(final_json)
|
||||
|
||||
except BaseException as msg:
|
||||
logging.writeToFile(str(msg))
|
||||
|
||||
## Find restore path
|
||||
|
||||
def findRestorePath(self):
|
||||
|
||||
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8 \
|
||||
or ProcessUtilities.decideDistro() == ProcessUtilities.ubuntu20:
|
||||
self.restoreTarget = '/'
|
||||
return 1
|
||||
else:
|
||||
if self.jobid.type[:8] == 'database':
|
||||
self.restoreTarget = '/usr/local/CyberCP/tmp/'
|
||||
elif self.jobid.type[:4] == 'data':
|
||||
self.restoreTarget = '/home/'
|
||||
elif self.jobid.type[:5] == 'email':
|
||||
self.restoreTarget = '/home/vmail/'
|
||||
elif self.jobid.type[:4] == 'meta':
|
||||
self.restoreTarget = '/home/%s/' % (self.website)
|
||||
|
||||
####
|
||||
|
||||
def getAWSData(self):
|
||||
key = self.backupDestinations.split('/')[-1]
|
||||
path = '/home/cyberpanel/aws/%s' % (key)
|
||||
secret = open(path, 'r').read()
|
||||
return key, secret
|
||||
|
||||
## Last argument delete is set when the snapshot is to be deleted from this repo, when this argument is set, any preceding argument is not used
|
||||
|
||||
def awsFunction(self, fType, backupPath=None, snapshotID=None, bType=None, delete=None):
|
||||
try:
|
||||
if fType == 'backup':
|
||||
key, secret = self.getAWSData()
|
||||
|
||||
# Define our excludes file for use with restic
|
||||
backupExcludesFile = '/home/%s/backup-exclude.conf' % (self.website.domain)
|
||||
resticBackupExcludeCMD = ' --exclude-file=%s' % (backupExcludesFile)
|
||||
|
||||
command = f'AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s restic -r s3:s3.amazonaws.com/%s backup %s --password-file %s --exclude /home/{self.website.domain}/logs --exclude /home/%s/backup --exclude /home/%s/incbackup' % (
|
||||
key, secret, self.website.domain, backupPath, self.passwordFile, self.website.domain, self.website.domain)
|
||||
|
||||
# If /home/%s/backup-exclude.conf file exists lets pass this to restic by appending the command to end.
|
||||
if os.path.isfile(backupExcludesFile):
|
||||
command = command + resticBackupExcludeCMD
|
||||
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
|
||||
if result.find('saved') == -1:
|
||||
logging.statusWriter(self.statusPath, '%s. [5009].' % (result), 1)
|
||||
return 0
|
||||
|
||||
snapShotid = result.split(' ')[-2]
|
||||
|
||||
if os.path.exists(ProcessUtilities.debugPath):
|
||||
logging.writeToFile(f'Snapshot id {snapShotid} from result {result}.')
|
||||
|
||||
|
||||
if bType == 'database':
|
||||
newSnapshot = JobSnapshots(job=self.jobid,
|
||||
type='%s:%s' % (bType, backupPath.split('/')[-1].rstrip('.sql')),
|
||||
snapshotid=snapShotid,
|
||||
destination=self.backupDestinations)
|
||||
else:
|
||||
newSnapshot = JobSnapshots(job=self.jobid, type='%s:%s' % (bType, backupPath),
|
||||
snapshotid=snapShotid,
|
||||
destination=self.backupDestinations)
|
||||
newSnapshot.save()
|
||||
return 1
|
||||
else:
|
||||
if self.reconstruct == 'remote':
|
||||
self.backupDestinations = self.backupDestinations
|
||||
|
||||
key, secret = self.getAWSData()
|
||||
|
||||
command = 'export RESTIC_PASSWORD=%s AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s && restic -r s3:s3.amazonaws.com/%s restore %s --target %s' % (
|
||||
self.passwordFile,
|
||||
key, secret, self.website, snapshotID, self.restoreTarget)
|
||||
|
||||
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
|
||||
if result.find('restoring') == -1:
|
||||
logging.statusWriter(self.statusPath, 'Failed: %s. [5009]' % (result), 1)
|
||||
return 0
|
||||
elif delete:
|
||||
|
||||
self.backupDestinations = self.jobid.destination
|
||||
|
||||
key, secret = self.getAWSData()
|
||||
|
||||
command = 'AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s restic -r s3:s3.amazonaws.com/%s forget %s --password-file %s' % (
|
||||
key, secret, self.website, snapshotID, self.passwordFile)
|
||||
|
||||
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
|
||||
if result.find('removed snapshot') > -1 or result.find('deleted') > -1:
|
||||
pass
|
||||
else:
|
||||
logging.statusWriter(self.statusPath, 'Failed: %s. [5009]' % (result), 1)
|
||||
return 0
|
||||
|
||||
command = 'AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s restic -r s3:s3.amazonaws.com/%s prune --password-file %s' % (
|
||||
key, secret, self.website, self.passwordFile)
|
||||
|
||||
ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
else:
|
||||
self.backupDestinations = self.jobid.destination
|
||||
|
||||
key, secret = self.getAWSData()
|
||||
|
||||
command = 'AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s restic -r s3:s3.amazonaws.com/%s restore %s --password-file %s --target %s' % (
|
||||
key, secret, self.website, snapshotID, self.passwordFile, self.restoreTarget)
|
||||
|
||||
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
|
||||
if result.find('restoring') == -1:
|
||||
logging.statusWriter(self.statusPath, 'Failed: %s. [5009]' % (result), 1)
|
||||
return 0
|
||||
|
||||
return 1
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath, "%s [88][5009]" % (str(msg)), 1)
|
||||
return 0
|
||||
|
||||
## Last argument delete is set when the snapshot is to be deleted from this repo, when this argument is set, any preceding argument is not used
|
||||
|
||||
def localFunction(self, backupPath, type, restore=None, delete=None):
|
||||
|
||||
if restore == None:
|
||||
# Define our excludes file for use with restic
|
||||
backupExcludesFile = '/home/%s/backup-exclude.conf' % (self.website.domain)
|
||||
resticBackupExcludeCMD = ' --exclude-file=%s' % (backupExcludesFile)
|
||||
|
||||
command = 'restic -r %s backup %s --password-file %s --exclude %s --exclude /home/%s/backup' % (
|
||||
self.repoPath, backupPath, self.passwordFile, self.repoPath, self.website.domain)
|
||||
# If /home/%s/backup-exclude.conf file exists lets pass this to restic by appending the command to end.
|
||||
if os.path.isfile(backupExcludesFile):
|
||||
command = command + resticBackupExcludeCMD
|
||||
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
|
||||
if result.find('saved') == -1:
|
||||
logging.statusWriter(self.statusPath, '%s. [5009].' % (result), 1)
|
||||
return 0
|
||||
|
||||
snapShotid = result.split(' ')[-2]
|
||||
|
||||
if type == 'database':
|
||||
newSnapshot = JobSnapshots(job=self.jobid,
|
||||
type='%s:%s' % (type, backupPath.split('/')[-1].rstrip('.sql')),
|
||||
snapshotid=snapShotid,
|
||||
destination=self.backupDestinations)
|
||||
else:
|
||||
newSnapshot = JobSnapshots(job=self.jobid, type='%s:%s' % (type, backupPath), snapshotid=snapShotid,
|
||||
destination=self.backupDestinations)
|
||||
newSnapshot.save()
|
||||
return 1
|
||||
elif delete:
|
||||
|
||||
repoLocation = '/home/%s/incbackup' % (self.website)
|
||||
|
||||
command = 'restic -r %s forget %s --password-file %s' % (repoLocation, self.jobid.snapshotid, self.passwordFile)
|
||||
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
|
||||
if result.find('removed snapshot') > -1 or result.find('deleted') > -1:
|
||||
pass
|
||||
else:
|
||||
logging.statusWriter(self.statusPath, 'Failed: %s. [5009]' % (result), 1)
|
||||
return 0
|
||||
|
||||
command = 'restic -r %s prune --password-file %s' % (repoLocation, self.passwordFile)
|
||||
ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
|
||||
return 1
|
||||
else:
|
||||
repoLocation = '/home/%s/incbackup' % (self.website)
|
||||
command = 'restic -r %s restore %s --target %s --password-file %s' % (
|
||||
repoLocation, self.jobid.snapshotid, self.restoreTarget, self.passwordFile)
|
||||
|
||||
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
|
||||
if result.find('restoring') == -1:
|
||||
logging.statusWriter(self.statusPath, 'Failed: %s. [5009]' % (result), 1)
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
## Last argument delete is set when the snapshot is to be deleted from this repo, when this argument is set, any preceding argument is not used
|
||||
|
||||
def sftpFunction(self, backupPath, type, restore=None, delete=None):
|
||||
return 0
|
||||
if restore == None:
|
||||
# Define our excludes file for use with restic
|
||||
backupExcludesFile = '/home/%s/backup-exclude.conf' % (self.website.domain)
|
||||
resticBackupExcludeCMD = ' --exclude-file=%s' % (backupExcludesFile)
|
||||
remotePath = '/home/backup/%s' % (self.website.domain)
|
||||
command = 'export PATH=${PATH}:/usr/bin && restic -r %s:%s backup %s --password-file %s --exclude %s --exclude /home/%s/backup' % (
|
||||
self.backupDestinations, remotePath, backupPath, self.passwordFile, self.repoPath, self.website.domain)
|
||||
# If /home/%s/backup-exclude.conf file exists lets pass this to restic by appending the command to end.
|
||||
if os.path.isfile(backupExcludesFile):
|
||||
command = command + resticBackupExcludeCMD
|
||||
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
|
||||
if result.find('saved') == -1:
|
||||
logging.statusWriter(self.statusPath, '%s. [5009].' % (result), 1)
|
||||
return 0
|
||||
|
||||
snapShotid = result.split(' ')[-2]
|
||||
|
||||
if type == 'database':
|
||||
newSnapshot = JobSnapshots(job=self.jobid,
|
||||
type='%s:%s' % (type, backupPath.split('/')[-1].rstrip('.sql')),
|
||||
snapshotid=snapShotid,
|
||||
destination=self.backupDestinations)
|
||||
else:
|
||||
newSnapshot = JobSnapshots(job=self.jobid, type='%s:%s' % (type, backupPath), snapshotid=snapShotid,
|
||||
destination=self.backupDestinations)
|
||||
newSnapshot.save()
|
||||
return 1
|
||||
elif delete:
|
||||
repoLocation = '/home/backup/%s' % (self.website)
|
||||
command = 'export PATH=${PATH}:/usr/bin && restic -r %s:%s forget %s --password-file %s' % (
|
||||
self.jobid.destination, repoLocation, self.jobid.snapshotid, self.passwordFile)
|
||||
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
|
||||
if result.find('removed snapshot') > -1 or result.find('deleted') > -1:
|
||||
pass
|
||||
else:
|
||||
logging.statusWriter(self.statusPath, 'Failed: %s. [5009]' % (result), 1)
|
||||
return 0
|
||||
|
||||
command = 'export PATH=${PATH}:/usr/bin && restic -r %s:%s prune --password-file %s' % (self.jobid.destination, repoLocation, self.passwordFile)
|
||||
ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
else:
|
||||
if self.reconstruct == 'remote':
|
||||
repoLocation = '/home/backup/%s' % (self.website)
|
||||
command = 'export RESTIC_PASSWORD=%s PATH=${PATH}:/usr/bin && restic -r %s:%s restore %s --target %s' % (
|
||||
self.passwordFile,
|
||||
self.backupDestinations, repoLocation, self.jobid, self.restoreTarget)
|
||||
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
if result.find('restoring') == -1:
|
||||
logging.statusWriter(self.statusPath, 'Failed: %s. [5009]' % (result), 1)
|
||||
return 0
|
||||
else:
|
||||
repoLocation = '/home/backup/%s' % (self.website)
|
||||
command = 'export PATH=${PATH}:/usr/bin && restic -r %s:%s restore %s --target %s --password-file %s' % (
|
||||
self.jobid.destination, repoLocation, self.jobid.snapshotid, self.restoreTarget, self.passwordFile)
|
||||
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
if result.find('restoring') == -1:
|
||||
logging.statusWriter(self.statusPath, 'Failed: %s. [5009]' % (result), 1)
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
def restoreData(self):
|
||||
try:
|
||||
|
||||
if self.reconstruct == 'remote':
|
||||
if self.backupDestinations[:4] == 'sftp':
|
||||
self.sftpFunction('none', 'none', 1)
|
||||
else:
|
||||
if self.awsFunction('restore', '', self.jobid) == 0:
|
||||
return 0
|
||||
else:
|
||||
if self.jobid.destination == 'local':
|
||||
return self.localFunction('none', 'none', 1)
|
||||
elif self.jobid.destination[:4] == 'sftp':
|
||||
return self.sftpFunction('none', 'none', 1)
|
||||
else:
|
||||
return self.awsFunction('restore', '', self.jobid.snapshotid)
|
||||
|
||||
return 1
|
||||
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath, "%s [138][5009]" % (str(msg)), 1)
|
||||
return 0
|
||||
|
||||
def restoreDatabase(self):
|
||||
try:
|
||||
|
||||
if self.reconstruct == 'remote':
|
||||
if self.backupDestinations[:4] == 'sftp':
|
||||
if self.sftpFunction('none', 'none', 1) == 0:
|
||||
return 0
|
||||
else:
|
||||
if self.awsFunction('restore', '', self.jobid) == 0:
|
||||
return 0
|
||||
|
||||
## Restore proper permissions
|
||||
|
||||
command = 'chown cyberpanel:cyberpanel /home/cyberpanel'
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
command = 'chmod 755 /home/cyberpanel'
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
##
|
||||
|
||||
if mysqlUtilities.mysqlUtilities.restoreDatabaseBackup(self.path.split('/')[-1].rstrip('.sql'),
|
||||
'/usr/local/CyberCP/tmp', 'dummy', 'dummy') == 0:
|
||||
raise BaseException
|
||||
else:
|
||||
|
||||
if self.jobid.destination == 'local':
|
||||
if self.localFunction('none', 'none', 1) == 0:
|
||||
return 0
|
||||
elif self.jobid.destination[:4] == 'sftp':
|
||||
if self.sftpFunction('none', 'none', 1) == 0:
|
||||
return 0
|
||||
else:
|
||||
if self.awsFunction('restore', '', self.jobid.snapshotid) == 0:
|
||||
return 0
|
||||
|
||||
|
||||
if mysqlUtilities.mysqlUtilities.restoreDatabaseBackup(self.jobid.type.split(':')[1].rstrip('.sql'),
|
||||
'/home/%s' % (self.website), 'dummy', 'dummy') == 0:
|
||||
raise BaseException('Can not restore database backup.')
|
||||
|
||||
try:
|
||||
if self.reconstruct == 'remote':
|
||||
os.remove('/usr/local/CyberCP/tmp/%s' % (self.path.split('/')[-1]))
|
||||
else:
|
||||
os.remove('/usr/local/CyberCP/tmp/%s.sql' % (self.jobid.type.split(':')[1]))
|
||||
os.remove('/home/%s/%s.sql' % (self.website.domain, self.jobid.type.split(':')[1]))
|
||||
except BaseException as msg:
|
||||
logging.writeToFile(str(msg))
|
||||
|
||||
return 1
|
||||
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath, "%s [160][5009]" % (str(msg)), 1)
|
||||
return 0
|
||||
|
||||
def restoreEmail(self):
|
||||
try:
|
||||
|
||||
if self.reconstruct == 'remote':
|
||||
if self.backupDestinations[:4] == 'sftp':
|
||||
if self.sftpFunction('none', 'none', 1) == 0:
|
||||
return 0
|
||||
else:
|
||||
if self.awsFunction('restore', '', self.jobid) == 0:
|
||||
return 0
|
||||
else:
|
||||
if self.jobid.destination == 'local':
|
||||
return self.localFunction('none', 'none', 1)
|
||||
elif self.jobid.destination[:4] == 'sftp':
|
||||
return self.sftpFunction('none', 'none', 1)
|
||||
else:
|
||||
return self.awsFunction('restore', '', self.jobid.snapshotid)
|
||||
|
||||
return 1
|
||||
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath, "%s [46][5009]" % (str(msg)), 1)
|
||||
return 0
|
||||
|
||||
def reconstructWithMeta(self):
|
||||
try:
|
||||
|
||||
if self.reconstruct == 'remote':
|
||||
if self.backupDestinations[:4] == 'sftp':
|
||||
if self.sftpFunction('none', 'none', 1) == 0:
|
||||
return 0
|
||||
else:
|
||||
if self.awsFunction('restore', '', self.jobid) == 0:
|
||||
return 0
|
||||
else:
|
||||
if self.jobid.destination == 'local':
|
||||
if self.localFunction('none', 'none', 1) == 0:
|
||||
return 0
|
||||
elif self.jobid.destination[:4] == 'sftp':
|
||||
if self.sftpFunction('none', 'none', 1) == 0:
|
||||
return 0
|
||||
else:
|
||||
if self.awsFunction('restore', '', self.jobid.snapshotid) == 0:
|
||||
return 0
|
||||
|
||||
metaPathNew = '/home/%s/meta.xml' % (self.website)
|
||||
execPath = "nice -n 10 /usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/restoreMeta.py"
|
||||
execPath = execPath + " submitRestore --metaPath %s --statusFile %s" % (metaPathNew, self.statusPath)
|
||||
result = ProcessUtilities.outputExecutioner(execPath)
|
||||
logging.statusWriter(self.statusPath, result, 1)
|
||||
|
||||
try:
|
||||
os.remove(metaPathNew)
|
||||
except:
|
||||
pass
|
||||
|
||||
return 1
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath, "%s [46][5009]" % (str(msg)), 1)
|
||||
return 0
|
||||
|
||||
def restorePoint(self):
|
||||
try:
|
||||
|
||||
self.statusPath = self.extraArgs['tempPath']
|
||||
self.website = self.extraArgs['website']
|
||||
jobid = self.extraArgs['jobid']
|
||||
self.reconstruct = self.extraArgs['reconstruct']
|
||||
|
||||
WebsiteObject = Websites.objects.get(domain=self.website)
|
||||
|
||||
self.externalApp = WebsiteObject.externalApp
|
||||
|
||||
if self.reconstruct == 'remote':
|
||||
|
||||
self.findRestorePath()
|
||||
|
||||
self.jobid = self.extraArgs['jobid']
|
||||
self.backupDestinations = self.extraArgs['backupDestinations']
|
||||
self.passwordFile = self.extraArgs['password']
|
||||
self.path = self.extraArgs['path']
|
||||
|
||||
if self.path.find('.sql') > -1:
|
||||
message = 'Restoring database..'
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
if self.restoreDatabase() == 0:
|
||||
return 0
|
||||
message = 'Database restored.'
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
elif self.path == '/home/%s' % (self.website):
|
||||
message = 'Restoring data..'
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
if self.restoreData() == 0:
|
||||
return 0
|
||||
message = 'Data restored..'
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
elif self.path.find('vmail') > -1:
|
||||
message = 'Restoring email..'
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
if self.restoreEmail() == 0:
|
||||
return 0
|
||||
message = 'Emails restored.'
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
elif self.path.find('meta.xml') > -1:
|
||||
message = 'Reconstructing with meta..'
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
if self.reconstructWithMeta() == 0:
|
||||
return 0
|
||||
message = 'Reconstructed'
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
else:
|
||||
|
||||
self.jobid = JobSnapshots.objects.get(pk=jobid)
|
||||
|
||||
self.findRestorePath()
|
||||
|
||||
message = 'Starting restore of %s for %s.' % (self.jobid.snapshotid, self.website)
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
self.passwordFile = '/home/%s/%s' % (self.website, self.website)
|
||||
|
||||
##
|
||||
|
||||
if self.jobid.type[:8] == 'database':
|
||||
message = 'Restoring database..'
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
self.restoreDatabase()
|
||||
message = 'Database restored.'
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
elif self.jobid.type[:4] == 'data':
|
||||
message = 'Restoring data..'
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
if self.restoreData() == 0:
|
||||
return 0
|
||||
message = 'Data restored.'
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
elif self.jobid.type[:5] == 'email':
|
||||
message = 'Restoring email..'
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
self.restoreEmail()
|
||||
message = 'Emails restored.'
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
elif self.jobid.type[:4] == 'meta':
|
||||
message = 'Reconstructing with meta..'
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
self.reconstructWithMeta()
|
||||
message = 'Reconstructed'
|
||||
logging.statusWriter(self.statusPath, message, 1)
|
||||
|
||||
logging.statusWriter(self.statusPath, 'Completed', 1)
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.extraArgs['tempPath'], str(msg), 1)
|
||||
|
||||
### Backup functions
|
||||
|
||||
def prepareBackupMeta(self):
|
||||
try:
|
||||
|
||||
## Use the meta function from backup utils for future improvements.
|
||||
|
||||
if os.path.exists(ProcessUtilities.debugPath):
|
||||
logging.writeToFile('Creating meta for %s. [IncBackupsControl.py]' % (self.website.domain))
|
||||
|
||||
from plogical.backupUtilities import backupUtilities
|
||||
status, message, metaPath = backupUtilities.prepareBackupMeta(self.website.domain, None, None, None, 0)
|
||||
|
||||
## meta generated
|
||||
|
||||
if status == 1:
|
||||
logging.statusWriter(self.statusPath, 'Meta data is ready..', 1)
|
||||
metaPathNew = '/home/%s/meta.xml' % (self.website.domain)
|
||||
|
||||
command = 'chown %s:%s %s' % (self.externalApp, self.externalApp, metaPath)
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
command = 'mv %s %s' % (metaPath, metaPathNew)
|
||||
ProcessUtilities.executioner(command, self.externalApp)
|
||||
return 1
|
||||
else:
|
||||
logging.statusWriter(self.statusPath, "%s [544][5009]" % (message), 1)
|
||||
return 0
|
||||
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath, "%s [548][5009]" % (str(msg)), 1)
|
||||
return 0
|
||||
|
||||
def backupData(self):
|
||||
try:
|
||||
logging.statusWriter(self.statusPath, 'Backing up data..', 1)
|
||||
backupPath = '/home/%s' % (self.website.domain)
|
||||
|
||||
if self.backupDestinations == 'local':
|
||||
if self.localFunction(backupPath, 'data') == 0:
|
||||
return 0
|
||||
elif self.backupDestinations[:4] == 'sftp':
|
||||
if self.sftpFunction(backupPath, 'data') == 0:
|
||||
return 0
|
||||
else:
|
||||
if self.awsFunction('backup', backupPath, '', 'data') == 0:
|
||||
return 0
|
||||
|
||||
logging.statusWriter(self.statusPath,
|
||||
'Data for %s backed to %s.' % (self.website.domain, self.backupDestinations), 1)
|
||||
return 1
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath, '%s. [IncJobs.backupData.223][5009]' % str(msg), 1)
|
||||
return 0
|
||||
|
||||
def backupDatabases(self):
|
||||
try:
|
||||
logging.statusWriter(self.statusPath, 'Backing up databases..', 1)
|
||||
|
||||
databases = self.website.databases_set.all()
|
||||
|
||||
for items in databases:
|
||||
|
||||
###
|
||||
|
||||
UploadPath = '/usr/local/CyberCP/tmp'
|
||||
|
||||
if not os.path.exists(UploadPath):
|
||||
command = 'mkdir %s' % (UploadPath)
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
command = 'chown cyberpanel:cyberpanel %s' % (UploadPath)
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
command = 'chmod 711 %s' % (UploadPath)
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
###
|
||||
|
||||
if mysqlUtilities.mysqlUtilities.createDatabaseBackup(items.dbName, UploadPath) == 0:
|
||||
return 0
|
||||
|
||||
if os.path.exists(ProcessUtilities.debugPath):
|
||||
logging.writeToFile(f'Backup created for DB Incscheduler.backupDatabases')
|
||||
|
||||
dbPath = '%s/%s.sql' % (UploadPath, items.dbName)
|
||||
dbPathNew = '/home/%s/%s.sql' % (self.website.domain, items.dbName)
|
||||
|
||||
command = 'cp %s %s' % (dbPath, dbPathNew)
|
||||
ProcessUtilities.executioner(command, self.externalApp)
|
||||
|
||||
if self.backupDestinations == 'local':
|
||||
if self.localFunction(dbPathNew, 'database') == 0:
|
||||
return 0
|
||||
elif self.backupDestinations[:4] == 'sftp':
|
||||
if self.sftpFunction(dbPathNew, 'database') == 0:
|
||||
return 0
|
||||
|
||||
else:
|
||||
if self.awsFunction('backup', dbPathNew, '', 'database') == 0:
|
||||
return 0
|
||||
|
||||
try:
|
||||
dbPath = '/usr/local/CyberCP/tmp/%s.sql' % (items.dbName)
|
||||
command = 'rm -f %s' % (dbPath)
|
||||
ProcessUtilities.executioner(command, self.externalApp)
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath,
|
||||
'Failed to delete database: %s. [IncJobs.backupDatabases.456]' % str(msg), 1)
|
||||
|
||||
return 1
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath, '%s. [IncJobs.backupDatabases.269][5009]' % str(msg), 1)
|
||||
return 0
|
||||
|
||||
def emailBackup(self):
|
||||
try:
|
||||
logging.statusWriter(self.statusPath, 'Backing up emails..', 1)
|
||||
|
||||
backupPath = '/home/vmail/%s' % (self.website.domain)
|
||||
|
||||
if os.path.exists(backupPath):
|
||||
|
||||
if self.backupDestinations == 'local':
|
||||
if self.localFunction(backupPath, 'email') == 0:
|
||||
return 0
|
||||
elif self.backupDestinations[:4] == 'sftp':
|
||||
if self.sftpFunction(backupPath, 'email') == 0:
|
||||
return 0
|
||||
else:
|
||||
if self.awsFunction('backup', backupPath, '', 'email') == 0:
|
||||
return 0
|
||||
|
||||
logging.statusWriter(self.statusPath,
|
||||
'Emails for %s backed to %s.' % (self.website.domain, self.backupDestinations), 1)
|
||||
return 1
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath, '%s. [IncJobs.emailBackup.269][5009]' % str(msg), 1)
|
||||
return 0
|
||||
|
||||
def metaBackup(self):
|
||||
try:
|
||||
logging.statusWriter(self.statusPath, 'Backing up meta..', 1)
|
||||
|
||||
backupPath = '/home/%s/meta.xml' % (self.website.domain)
|
||||
|
||||
if self.backupDestinations == 'local':
|
||||
if self.localFunction(backupPath, 'meta') == 0:
|
||||
return 0
|
||||
elif self.backupDestinations[:4] == 'sftp':
|
||||
if self.sftpFunction(backupPath, 'meta') == 0:
|
||||
return 0
|
||||
else:
|
||||
if self.awsFunction('backup', backupPath, '', 'meta') == 0:
|
||||
return 0
|
||||
|
||||
logging.statusWriter(self.statusPath,
|
||||
'Meta for %s backed to %s.' % (self.website.domain, self.backupDestinations), 1)
|
||||
return 1
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath, '%s. [IncJobs.metaBackup.269][5009]' % str(msg), 1)
|
||||
return 0
|
||||
|
||||
def initiateRepo(self):
|
||||
try:
|
||||
logging.statusWriter(self.statusPath, 'Will first initiate backup repo..', 1)
|
||||
|
||||
if self.backupDestinations == 'local':
|
||||
command = 'restic init --repo %s --password-file %s' % (self.repoPath, self.passwordFile)
|
||||
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
|
||||
if os.path.exists(ProcessUtilities.debugPath):
|
||||
logging.writeToFile(result)
|
||||
|
||||
if result.find('config file already exists') == -1:
|
||||
logging.statusWriter(self.statusPath, result, 1)
|
||||
|
||||
elif self.backupDestinations[:4] == 'sftp':
|
||||
remotePath = '/home/backup/%s' % (self.website.domain)
|
||||
command = 'export PATH=${PATH}:/usr/bin && restic init --repo %s:%s --password-file %s' % (
|
||||
self.backupDestinations, remotePath, self.passwordFile)
|
||||
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
|
||||
if os.path.exists(ProcessUtilities.debugPath):
|
||||
logging.writeToFile(result)
|
||||
|
||||
if result.find('config file already exists') == -1:
|
||||
logging.statusWriter(self.statusPath, result, 1)
|
||||
else:
|
||||
key, secret = self.getAWSData()
|
||||
command = 'AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s restic -r s3:s3.amazonaws.com/%s init --password-file %s' % (
|
||||
key, secret, self.website.domain, self.passwordFile)
|
||||
result = ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
|
||||
if os.path.exists(ProcessUtilities.debugPath):
|
||||
logging.writeToFile(result)
|
||||
|
||||
if result.find('config file already exists') == -1:
|
||||
logging.statusWriter(self.statusPath, result, 1)
|
||||
|
||||
logging.statusWriter(self.statusPath,
|
||||
'Repo %s initiated for %s.' % (self.backupDestinations, self.website.domain), 1)
|
||||
return 1
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath, '%s. [IncJobs.initiateRepo.47][5009]' % str(msg), 1)
|
||||
return 0
|
||||
|
||||
def sendEmail(self, password):
|
||||
SUBJECT = "Backup Repository password for %s" % (self.website.domain)
|
||||
text = """Password: %s
|
||||
This is password for your incremental backup repository, please save it in safe place as it will be required when you want to restore backup for this site on remote server.
|
||||
""" % (password)
|
||||
|
||||
sender = 'cyberpanel@%s' % (self.website.domain)
|
||||
TO = [self.website.adminEmail]
|
||||
message = """\
|
||||
From: %s
|
||||
To: %s
|
||||
Subject: %s
|
||||
|
||||
%s
|
||||
""" % (sender, ", ".join(TO), SUBJECT, text)
|
||||
mailUtilities.SendEmail(sender, TO, message)
|
||||
|
||||
def createBackup(self):
|
||||
|
||||
try:
|
||||
|
||||
self.statusPath = self.extraArgs['tempPath']
|
||||
website = self.extraArgs['website']
|
||||
self.backupDestinations = self.extraArgs['backupDestinations']
|
||||
websiteData = self.extraArgs['websiteData']
|
||||
websiteEmails = self.extraArgs['websiteEmails']
|
||||
websiteDatabases = self.extraArgs['websiteDatabases']
|
||||
|
||||
### Checking if restic is installed before moving on
|
||||
|
||||
command = 'restic'
|
||||
|
||||
if ProcessUtilities.outputExecutioner(command).find('restic is a backup program which') == -1:
|
||||
try:
|
||||
|
||||
CentOSPath = '/etc/redhat-release'
|
||||
|
||||
if os.path.exists(CentOSPath):
|
||||
command = 'yum install -y yum-plugin-copr'
|
||||
ProcessUtilities.executioner(command)
|
||||
command = 'yum copr enable -y copart/restic'
|
||||
ProcessUtilities.executioner(command)
|
||||
command = 'yum install -y restic'
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
else:
|
||||
command = 'apt-get update -y'
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
command = 'apt-get install restic -y'
|
||||
ProcessUtilities.executioner(command)
|
||||
|
||||
except:
|
||||
logging.statusWriter(self.statusPath,
|
||||
'It seems restic is not installed, for incremental backups to work '
|
||||
'restic must be installed. You can manually install restic using this '
|
||||
'guide -> https://go.cyberpanel.net/restic. [5009]', 1)
|
||||
pass
|
||||
|
||||
return 0
|
||||
|
||||
## Restic check completed.
|
||||
|
||||
self.website = Websites.objects.get(domain=website)
|
||||
self.externalApp = self.website.externalApp
|
||||
|
||||
self.jobid = IncJob(website=self.website)
|
||||
self.jobid.save()
|
||||
|
||||
self.passwordFile = '/home/%s/%s' % (self.website.domain, self.website.domain)
|
||||
|
||||
self.repoPath = '/home/%s/incbackup' % (self.website.domain)
|
||||
|
||||
command = 'ls -la %s' % (self.passwordFile)
|
||||
output = ProcessUtilities.outputExecutioner(command, self.externalApp)
|
||||
|
||||
if output.find('No such file or directory') > -1:
|
||||
password = randomPassword.generate_pass()
|
||||
command = 'echo "%s" > %s' % (password, self.passwordFile)
|
||||
ProcessUtilities.executioner(command, self.externalApp, True)
|
||||
|
||||
command = 'chmod 600 %s' % (self.passwordFile)
|
||||
ProcessUtilities.executioner(command, self.externalApp)
|
||||
|
||||
self.sendEmail(password)
|
||||
|
||||
## Completed password generation
|
||||
|
||||
if self.initiateRepo() == 0:
|
||||
return 0
|
||||
|
||||
if self.prepareBackupMeta() == 0:
|
||||
return 0
|
||||
|
||||
if websiteData:
|
||||
if self.backupData() == 0:
|
||||
return 0
|
||||
|
||||
if websiteDatabases:
|
||||
if self.backupDatabases() == 0:
|
||||
return 0
|
||||
|
||||
if websiteEmails:
|
||||
if self.emailBackup() == 0:
|
||||
return 0
|
||||
|
||||
## Backup job done
|
||||
|
||||
self.metaBackup()
|
||||
|
||||
metaPathNew = '/home/%s/meta.xml' % (self.website.domain)
|
||||
|
||||
try:
|
||||
command = 'rm -f %s' % (metaPathNew)
|
||||
ProcessUtilities.executioner(command)
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath,
|
||||
'Failed to delete meta file: %s. [IncJobs.createBackup.591]' % str(msg), 1)
|
||||
|
||||
logging.statusWriter(self.statusPath, 'Completed', 1)
|
||||
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath,
|
||||
'Failed to create incremental backup: %s. [5009][IncJobs.createBackup.913]' % str(msg), 1)
|
||||
|
||||
|
||||
### Delete Snapshot
|
||||
|
||||
def DeleteSnapShot(self, inc_job):
|
||||
try:
|
||||
|
||||
self.statusPath = logging.fileName
|
||||
|
||||
job_snapshots = inc_job.jobsnapshots_set.all()
|
||||
|
||||
### Fetch the website name from JobSnapshot object and set these variable as they are needed in called functions below
|
||||
|
||||
self.website = job_snapshots[0].job.website.domain
|
||||
self.externalApp = job_snapshots[0].job.website.externalApp
|
||||
self.passwordFile = '/home/%s/%s' % (self.website, self.website)
|
||||
|
||||
for job_snapshot in job_snapshots:
|
||||
|
||||
## Functions above use the self.jobid varilable to extract information about this snapshot, so this below variable needs to be set
|
||||
|
||||
self.jobid = job_snapshot
|
||||
|
||||
if self.jobid.destination == 'local':
|
||||
self.localFunction('none', 'none', 0, 1)
|
||||
elif self.jobid.destination[:4] == 'sftp':
|
||||
self.sftpFunction('none', 'none', 0, 1)
|
||||
else:
|
||||
self.awsFunction('restore', '', self.jobid.snapshotid, None, 1)
|
||||
|
||||
return 1
|
||||
|
||||
except BaseException as msg:
|
||||
logging.statusWriter(self.statusPath, "%s [903:DeleteSnapShot][5009]" % (str(msg)), 1)
|
||||
return 0
|
||||
17
IncBackups/IncScheduler.py
Normal file
17
IncBackups/IncScheduler.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import argparse
|
||||
import sys
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
from plogical.processUtilities import ProcessUtilities
|
||||
|
||||
def main():
|
||||
|
||||
parser = argparse.ArgumentParser(description='CyberPanel Installer')
|
||||
parser.add_argument('function', help='Specific a function to call!')
|
||||
args = parser.parse_args()
|
||||
|
||||
command = f"/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py '{args.function}'"
|
||||
ProcessUtilities.normalExecutioner(command)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
0
IncBackups/__init__.py
Normal file
0
IncBackups/__init__.py
Normal file
6
IncBackups/admin.py
Normal file
6
IncBackups/admin.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
8
IncBackups/apps.py
Normal file
8
IncBackups/apps.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class IncbackupsConfig(AppConfig):
|
||||
name = 'IncBackups'
|
||||
0
IncBackups/migrations/__init__.py
Normal file
0
IncBackups/migrations/__init__.py
Normal file
46
IncBackups/models.py
Normal file
46
IncBackups/models.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from django.db import models
|
||||
from websiteFunctions.models import Websites
|
||||
from loginSystem.models import Administrator
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class IncJob(models.Model):
|
||||
website = models.ForeignKey(Websites, on_delete=models.CASCADE)
|
||||
date = models.DateTimeField(default=datetime.now, blank=True)
|
||||
|
||||
|
||||
class JobSnapshots(models.Model):
|
||||
job = models.ForeignKey(IncJob, on_delete=models.CASCADE)
|
||||
type = models.CharField(max_length=300)
|
||||
snapshotid = models.CharField(max_length=50)
|
||||
destination = models.CharField(max_length=200, default='')
|
||||
|
||||
|
||||
class BackupJob(models.Model):
|
||||
destination = models.CharField(max_length=300)
|
||||
frequency = models.CharField(max_length=50)
|
||||
websiteData = models.IntegerField()
|
||||
websiteDatabases = models.IntegerField()
|
||||
websiteDataEmails = models.IntegerField()
|
||||
retention = models.IntegerField(default=0) # 0 being unlimited retention
|
||||
|
||||
|
||||
class JobSites(models.Model):
|
||||
job = models.ForeignKey(BackupJob, on_delete=models.CASCADE)
|
||||
website = models.CharField(max_length=300)
|
||||
|
||||
|
||||
class OneClickBackups(models.Model):
|
||||
owner = models.ForeignKey(Administrator, on_delete=models.PROTECT)
|
||||
planName = models.CharField(max_length=100)
|
||||
months = models.CharField(max_length=100)
|
||||
price = models.CharField(max_length=100)
|
||||
customer = models.CharField(max_length=300)
|
||||
subscription = models.CharField(max_length=300, unique=True)
|
||||
sftpUser = models.CharField(max_length=100)
|
||||
config = models.TextField(default='{}')
|
||||
date = models.DateTimeField(default=datetime.now, blank=True)
|
||||
state = models.IntegerField(default=0)
|
||||
|
||||
|
||||
|
||||
2468
IncBackups/static/IncBackups/IncBackups.js
Normal file
2468
IncBackups/static/IncBackups/IncBackups.js
Normal file
File diff suppressed because it is too large
Load Diff
234
IncBackups/templates/IncBackups/ConfigureV2Backup.html
Normal file
234
IncBackups/templates/IncBackups/ConfigureV2Backup.html
Normal file
@@ -0,0 +1,234 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Configure V2 Backup" %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
{% if BackupStat %}
|
||||
<div class="container">
|
||||
|
||||
<div ng-controller="ConfigureV2Backup" class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Configure v2 Backup Destinations" %} <img ng-hide="cyberpanelLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
|
||||
<form action="/" class="form-horizontal bordered-row">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Website" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select id="selwebsite" ng-change="selectwebsite()" ng-model="selwebsite"
|
||||
class="form-control">
|
||||
{% for items in websiteList %}
|
||||
<option value="{{ items }}">{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Backup Type" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select ng-change="selectbackuptype()" ng-model="v2backuptype" class="form-control">
|
||||
<option>SFTP</option>
|
||||
<option>GDrive</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<div id="GdriveModal" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog modal-lg">
|
||||
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×
|
||||
</button>
|
||||
<h4 class="modal-title">{% trans "Set up account" %}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<form name="containerSettingsForm" action="/" class="form-horizontal">
|
||||
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Account Name" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="accountName" type="text" class="form-control"
|
||||
ng-model="accountName">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<p style="margin: 1%" class="text-center"><a
|
||||
href="https://rclone.org/drive/#making-your-own-client-id">Learn
|
||||
how to create this.</a>. These field can be left empty but backups
|
||||
may not work then. </p>
|
||||
<label class="col-sm-3 control-label">{% trans "client_id" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="client_id" type="text" class="form-control"
|
||||
ng-model="client_id">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "client_secret" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="client_secret" type="text" class="form-control"
|
||||
ng-model="client_secret">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary"
|
||||
ng-click="setupAccount()">Save <img
|
||||
ng-hide="cyberpanelLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</button>
|
||||
<button type="button" ng-disabled="savingSettings"
|
||||
class="btn btn-default" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="SFTPModal" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog modal-lg">
|
||||
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×
|
||||
</button>
|
||||
<h4 class="modal-title">{% trans "Set up account" %}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<form name="containerSettingsForm" action="/" class="form-horizontal">
|
||||
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Repo Name" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="reponame" type="text" class="form-control"
|
||||
ng-model="reponame">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Host Name" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="accountName" type="text" class="form-control"
|
||||
ng-model="hostName">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Username" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="accountName" type="text" class="form-control"
|
||||
ng-model="UserName">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Password" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="accountName" type="text" class="form-control"
|
||||
ng-model="sfptpasswd">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "SSH Port" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="sshPort" type="text" class="form-control"
|
||||
ng-model="sshPort" value="Default SSH Port is 22">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary"
|
||||
ng-click="ConfigerSFTP()">Save <img
|
||||
ng-hide="cyberpanelLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</button>
|
||||
<button type="button" ng-disabled="savingSettings"
|
||||
class="btn btn-default" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
{% else %}
|
||||
<style>
|
||||
.feturecard {
|
||||
width: 30%;
|
||||
padding: 46px;
|
||||
margin: 15px
|
||||
}
|
||||
</style>
|
||||
<div class="container">
|
||||
<div id="page-title" align="center">
|
||||
<h1 style="color: black"><strong>Backups v2 - Incremental Backups! </strong></h1>
|
||||
<p style="font-size: 15px; color: black; margin-top: 1%">Is your website's data protection strategy up
|
||||
to par? Are you tired of dealing with slow and unreliable backup solutions that don't offer the
|
||||
level of robustness you need? <br><br>
|
||||
|
||||
Introducing CyberPanel's latest feature, Backups v2! With Backups v2, you can ensure that your
|
||||
website's data is protected like never before. Our advanced incremental backup system utilizes
|
||||
rclone, a powerful backup tool that supports over 30+ backends, including popular cloud storage
|
||||
providers like Google Drive, Dropbox, Amazon S3, and more!<br><br>
|
||||
|
||||
Gone are the days of slow and cumbersome full backups. With Backups v2, you can take advantage of
|
||||
incremental backups that only capture changes to your data, making the process lightning-fast and
|
||||
highly efficient. This means you can save time and bandwidth while still maintaining the highest
|
||||
level of data integrity.<br><br>
|
||||
|
||||
Whether you have a small blog or a large e-commerce website, Backups v2 is tailored to meet your
|
||||
needs. With its robustness and versatility, you can have peace of mind knowing that your website's
|
||||
data is backed up securely and can be easily restored whenever you need it.
|
||||
<br></p>
|
||||
</div>
|
||||
<p align="center">
|
||||
<iframe width="788.54" height="443" src="https://www.youtube.com/embed/7dI1013xvUc"
|
||||
title="YouTube video player" frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen></iframe>
|
||||
</p>
|
||||
<div style="margin-top: 2%">
|
||||
<stripe-pricing-table pricing-table-id="prctbl_1PhTRPRs6rG0dTDlJZMzTw0k"
|
||||
publishable-key="pk_live_51PgodkRs6rG0dTDl3jCUgxjyjI983GmCdHjzuLfZIsssmDLgKgnXnwQghVCctKMNFIC5K4oMcviTFrHf1ytsYZGa00AGySiWlF">
|
||||
</stripe-pricing-table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% endblock %}
|
||||
162
IncBackups/templates/IncBackups/CreateV2Backup.html
Normal file
162
IncBackups/templates/IncBackups/CreateV2Backup.html
Normal file
@@ -0,0 +1,162 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Create v2 Backup" %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
<div class="container">
|
||||
<div id="page-title">
|
||||
<h2>{% trans "Create V2 Backup" %} - <a target="_blank" href="http://go.cyberpanel.net/backup"
|
||||
style="height: 23px;line-height: 21px;"
|
||||
class="btn btn-border btn-alt border-red btn-link font-red"
|
||||
title=""><span>{% trans "Backup Docs" %}</span></a></h2>
|
||||
<p>{% trans "This page can be used to create your backup" %}</p>
|
||||
</div>
|
||||
|
||||
<div ng-controller="CreateV2Backup" class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Create v2 Backup" %} <img ng-hide="backupLoading" src="{% static 'images/loading.gif' %}">
|
||||
- <a href="{% url 'RestoreV2backupSite' %}">Restore Backups</a>
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
<p align="center" style="margin-top: 2%; margin-bottom: 2%">
|
||||
<iframe width="788.54" height="443" src="https://www.youtube.com/embed/7dI1013xvUc"
|
||||
title="YouTube video player" frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen></iframe>
|
||||
</p>
|
||||
|
||||
<form class="form-horizontal bordered-row">
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Website" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select id="create-backup-select" ng-change="selectwebsite()" ng-model="selwebsite"
|
||||
class="form-control">
|
||||
{% for items in websiteList %}
|
||||
<option value="{{ items }}">{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Repo" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select id="reposelectbox" ng-change="selectrepo()" ng-model="testhabbi"
|
||||
class="form-control">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Backup Contents" %}</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="websiteData" type="checkbox" value="">
|
||||
Data
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="websiteDatabases" type="checkbox" value="">
|
||||
Databases
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="websiteEmails" type="checkbox" value="">
|
||||
Emails
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!---
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="websiteSSLs" type="checkbox" value="">
|
||||
SSL Certificates
|
||||
</label>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans " " %} </label>
|
||||
<div class="col-sm-6">
|
||||
<button class="btn" id="CreateV2BackupButton" ng-click="CreateV2BackupButton()"
|
||||
style="border-radius: 6px;background-color: #3447b7;color: white !important;">
|
||||
Create Backup
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div ng-hide="installationProgress" class="form-group">
|
||||
<label class="col-sm-2 control-label"></label>
|
||||
<div class="col-sm-7">
|
||||
|
||||
<div class="alert alert-success text-center">
|
||||
<h2>{$ currentStatus $}</h2>
|
||||
</div>
|
||||
|
||||
<div class="progress">
|
||||
<div id="installProgress" class="progress-bar" role="progressbar" aria-valuenow="70"
|
||||
aria-valuemin="0" aria-valuemax="100" style="width:0%">
|
||||
<span class="sr-only">70% Complete</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="errorMessageBox" class="alert alert-danger">
|
||||
<p>{% trans "Error message:" %} {$ errorMessage $}</p>
|
||||
</div>
|
||||
|
||||
<div ng-hide="success" class="alert alert-success">
|
||||
<p>{% trans "Backup succesfully." %}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div ng-hide="couldNotConnect" class="alert alert-danger">
|
||||
<p>{% trans "Could not connect to server. Please refresh this page." %}</p>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationProgress" class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-disabled="goBackDisable" ng-click="goBack()"
|
||||
class="btn btn-primary btn-lg">{% trans "Go Back" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
121
IncBackups/templates/IncBackups/DeleteV2repo.html
Normal file
121
IncBackups/templates/IncBackups/DeleteV2repo.html
Normal file
@@ -0,0 +1,121 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Delete v2 Backup" %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div id="page-title">
|
||||
<h2>{% trans "Delete V2 Repo" %} - <a target="_blank" href="http://go.cyberpanel.net/backup"
|
||||
style="height: 23px;line-height: 21px;"
|
||||
class="btn btn-border btn-alt border-red btn-link font-red"
|
||||
title=""><span>{% trans "Backup Docs" %}</span></a></h2>
|
||||
<p>{% trans "This page can be used to delete your backup repo" %}</p>
|
||||
</div>
|
||||
|
||||
<div ng-controller="DeleteBackuprepo" class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Create v2 Backup" %} <img ng-hide="backupLoading" src="{% static 'images/loading.gif' %}">
|
||||
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
|
||||
|
||||
<form class="form-horizontal bordered-row">
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Website" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select id="create-backup-select" ng-change="selectwebsite()" ng-model="selwebsite"
|
||||
class="form-control">
|
||||
{% for items in websiteList %}
|
||||
<option value="{{ items }}">{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Repo" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select id="reposelectbox" ng-change="selectrepo()" ng-model="testhabbi"
|
||||
class="form-control">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans " " %} </label>
|
||||
<div class="col-sm-6">
|
||||
<button class="btn" id="DeleteV2BackupButton" ng-click="DeleteV2BackupButton()"
|
||||
style="border-radius: 6px;background-color: #3447b7;color: white !important;">
|
||||
Delete Backup repo
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div ng-hide="installationProgress" class="form-group">
|
||||
<label class="col-sm-2 control-label"></label>
|
||||
<div class="col-sm-7">
|
||||
|
||||
<div class="alert alert-success text-center">
|
||||
<h2>{$ currentStatus $}</h2>
|
||||
</div>
|
||||
|
||||
<div class="progress">
|
||||
<div id="installProgress" class="progress-bar" role="progressbar" aria-valuenow="70"
|
||||
aria-valuemin="0" aria-valuemax="100" style="width:0%">
|
||||
<span class="sr-only">70% Complete</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="errorMessageBox" class="alert alert-danger">
|
||||
<p>{% trans "Error message:" %} {$ errorMessage $}</p>
|
||||
</div>
|
||||
|
||||
<div ng-hide="success" class="alert alert-success">
|
||||
<p>{% trans "Backup succesfully." %}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div ng-hide="couldNotConnect" class="alert alert-danger">
|
||||
<p>{% trans "Could not connect to server. Please refresh this page." %}</p>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationProgress" class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-disabled="goBackDisable" ng-click="goBack()"
|
||||
class="btn btn-primary btn-lg">{% trans "Go Back" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
301
IncBackups/templates/IncBackups/RestoreV2Backup.html
Normal file
301
IncBackups/templates/IncBackups/RestoreV2Backup.html
Normal file
@@ -0,0 +1,301 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Backup Website" %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
{% if BackupStat %}
|
||||
<div class="container">
|
||||
<div id="page-title">
|
||||
<h2>{% trans "Restore V2 Backups" %} - <a target="_blank" href="http://go.cyberpanel.net/backup"
|
||||
style="height: 23px;line-height: 21px;"
|
||||
class="btn btn-border btn-alt border-red btn-link font-red"
|
||||
title=""><span>{% trans "Backup Docs" %}</span></a></h2>
|
||||
<p>{% trans "This page can be used to restore your backup sites" %}</p>
|
||||
</div>
|
||||
|
||||
<div ng-controller="restorev2backupoage" class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Restore V2 Backups" %} <img ng-hide="backupLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
<form action="/IncBackups/static" class="form-horizontal bordered-row">
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Website" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select id="create-backup-select" ng-change="selectwebsite()" ng-model="selwebsite"
|
||||
class="form-control">
|
||||
{% for items in websiteList %}
|
||||
<option value="{{ items }}">{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Repo" %} </label>
|
||||
<div class="col-sm-6">
|
||||
|
||||
<select ng-change="selectrepo()" ng-model="testhabbi"
|
||||
class="form-control">
|
||||
<option ng-repeat="repo in repos track by $index">{$ repo $}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div ng-hide="runningSnapshot" class="form-group">
|
||||
|
||||
<div class="col-sm-12">
|
||||
|
||||
<table id="snapshotstable" class="table">
|
||||
<thead>
|
||||
<tr style="border-bottom: #cccccc solid 1px!important;">
|
||||
<th style="border: none">{% trans "Snapshot ID" %}</th>
|
||||
<th style="border: none">{% trans "Time" %}</th>
|
||||
<th style="border: none">{% trans "Action" %} ></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="listsnapshots">
|
||||
|
||||
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
<div id="RestoreSnapshotPath" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog modal-lg">
|
||||
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content" style="height: 80%">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×
|
||||
</button>
|
||||
<h4 id="restore_header_text" class="modal-title">{% trans "Restore" %} <img
|
||||
ng-hide="backupLoading" src="{% static 'images/loading.gif' %}"></h4>
|
||||
</div>
|
||||
<div class="modal-body" style="">
|
||||
|
||||
<span style="font-weight: bolder">ID: <span style="font-weight: normal"
|
||||
id="Snapshot_id"></span></span><br>
|
||||
<span style="font-weight: bolder">PATH: <span style="font-weight: normal"
|
||||
id="Snapshot_Path_id"></span></span><br>
|
||||
<span style="display: flex;justify-content: center;margin-top: 20px;font-weight: bolder">{% trans "Are you ready to restore the backup? This may overwrite existing files." %} </span>
|
||||
<div style="display: flex;justify-content: center;margin-top: 10px;margin-bottom: 20px">
|
||||
<button type="button" class="btn btn-primary "
|
||||
style="margin-right: 20px!important;"
|
||||
ng-click="RestorePathV2()">Yes <img
|
||||
ng-hide="backupLoading"
|
||||
src="">
|
||||
</button>
|
||||
<button type="button" ng-disabled="savingSettings"
|
||||
class="btn btn-default mx-3" data-dismiss="modal">
|
||||
No
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationProgress" class="form-group">
|
||||
<label class="col-sm-12 control-label"></label>
|
||||
<div class="col-sm-12">
|
||||
|
||||
<div class="alert alert-success text-center">
|
||||
{# <h2>{$ currentStatus $}</h2>#}
|
||||
<h2 class="ng-binding">Successfully Restored</h2>
|
||||
</div>
|
||||
|
||||
<div class="progress">
|
||||
<div id="installProgress" class="progress-bar" role="progressbar"
|
||||
aria-valuenow="70"
|
||||
aria-valuemin="0" aria-valuemax="100" style="width:0%">
|
||||
<span class="sr-only">70% Complete</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="errorMessageBox" class="alert alert-danger">
|
||||
<p>{% trans "Error message:" %} {$ errorMessage $}</p>
|
||||
</div>
|
||||
|
||||
<div ng-hide="success" class="alert alert-success">
|
||||
<p>{% trans "Restored succesfully." %}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div ng-hide="couldNotConnect" class="alert alert-danger">
|
||||
<p>{% trans "Could not connect to server. Please refresh this page." %}</p>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationProgress" class="form-group">
|
||||
<label class="col-sm-12 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-disabled="goBackDisable" ng-click="goBack()"
|
||||
class="btn btn-primary btn-lg">{% trans "Go Back" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
<button type="button" ng-disabled="savingSettings"
|
||||
class="btn btn-default" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div id="DeleteSnapshotmodelv2" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog modal-lg">
|
||||
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content" style="height: 80%">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×
|
||||
</button>
|
||||
<h4 id="restore_header_text" class="modal-title">{% trans "Delete" %} <img
|
||||
ng-hide="backupLoading" src="{% static 'images/loading.gif' %}"></h4>
|
||||
</div>
|
||||
<div class="modal-body" style="">
|
||||
|
||||
<span style="font-weight: bolder"><span style="font-weight: bolder">ID: <span
|
||||
style="font-weight: normal"
|
||||
id="Snapshot_id_delete"></span></span><br>
|
||||
<span style="display: flex;justify-content: center;margin-top: 20px;font-weight: bolder">{% trans "Are you ready to delete this snapshot? This process can not be undone." %} </span>
|
||||
<div style="display: flex;justify-content: center;margin-top: 10px;margin-bottom: 20px">
|
||||
<button type="button" class="btn btn-primary "
|
||||
style="margin-right: 20px!important;"
|
||||
ng-click="DeleteSnapshotV2Final()">Yes <img
|
||||
ng-hide="backupLoading"
|
||||
src="">
|
||||
</button>
|
||||
<button type="button" ng-disabled="savingSettings"
|
||||
class="btn btn-default mx-3" data-dismiss="modal">
|
||||
No
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationProgress" class="form-group">
|
||||
<label class="col-sm-12 control-label"></label>
|
||||
<div class="col-sm-12">
|
||||
|
||||
<div class="alert alert-success text-center">
|
||||
{# <h2>{$ currentStatus $}</h2>#}
|
||||
<h2 class="ng-binding">Successfully Restored</h2>
|
||||
</div>
|
||||
|
||||
<div class="progress">
|
||||
<div id="installProgress" class="progress-bar" role="progressbar"
|
||||
aria-valuenow="70"
|
||||
aria-valuemin="0" aria-valuemax="100" style="width:0%">
|
||||
<span class="sr-only">70% Complete</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="errorMessageBox" class="alert alert-danger">
|
||||
<p>{% trans "Error message:" %} {$ errorMessage $}</p>
|
||||
</div>
|
||||
|
||||
<div ng-hide="success" class="alert alert-success">
|
||||
<p>{% trans "Restored succesfully." %}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div ng-hide="couldNotConnect" class="alert alert-danger">
|
||||
<p>{% trans "Could not connect to server. Please refresh this page." %}</p>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationProgress" class="form-group">
|
||||
<label class="col-sm-12 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-disabled="goBackDisable" ng-click="goBack()"
|
||||
class="btn btn-primary btn-lg">{% trans "Go Back" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
<button type="button" ng-disabled="savingSettings"
|
||||
class="btn btn-default" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
{% else %}
|
||||
<style>
|
||||
.feturecard {
|
||||
width: 30%;
|
||||
padding: 46px;
|
||||
margin: 15px
|
||||
}
|
||||
</style>
|
||||
<div class="container">
|
||||
<div id="page-title" align="center">
|
||||
<h1 style="color: black"><strong>Backups v2 - Incremental Backups! </strong></h1>
|
||||
<p style="font-size: 15px; color: black; margin-top: 1%">Is your website's data protection strategy up
|
||||
to par? Are you tired of dealing with slow and unreliable backup solutions that don't offer the
|
||||
level of robustness you need? <br><br>
|
||||
|
||||
Introducing CyberPanel's latest feature, Backups v2! With Backups v2, you can ensure that your
|
||||
website's data is protected like never before. Our advanced incremental backup system utilizes
|
||||
rclone, a powerful backup tool that supports over 30+ backends, including popular cloud storage
|
||||
providers like Google Drive, Dropbox, Amazon S3, and more!<br><br>
|
||||
|
||||
Gone are the days of slow and cumbersome full backups. With Backups v2, you can take advantage of
|
||||
incremental backups that only capture changes to your data, making the process lightning-fast and
|
||||
highly efficient. This means you can save time and bandwidth while still maintaining the highest
|
||||
level of data integrity.<br><br>
|
||||
|
||||
Whether you have a small blog or a large e-commerce website, Backups v2 is tailored to meet your
|
||||
needs. With its robustness and versatility, you can have peace of mind knowing that your website's
|
||||
data is backed up securely and can be easily restored whenever you need it.
|
||||
<br></p>
|
||||
</div>
|
||||
<p align="center">
|
||||
<iframe width="788.54" height="443" src="https://www.youtube.com/embed/7dI1013xvUc"
|
||||
title="YouTube video player" frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen></iframe>
|
||||
</p>
|
||||
<div style="margin-top: 2%">
|
||||
<stripe-pricing-table pricing-table-id="prctbl_1PhTRPRs6rG0dTDlJZMzTw0k"
|
||||
publishable-key="pk_live_51PgodkRs6rG0dTDl3jCUgxjyjI983GmCdHjzuLfZIsssmDLgKgnXnwQghVCctKMNFIC5K4oMcviTFrHf1ytsYZGa00AGySiWlF">
|
||||
</stripe-pricing-table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% endblock %}
|
||||
263
IncBackups/templates/IncBackups/ScheduleV2Backup.html
Normal file
263
IncBackups/templates/IncBackups/ScheduleV2Backup.html
Normal file
@@ -0,0 +1,263 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Schedule v2 Backup" %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
{% if BackupStat %}
|
||||
<div class="container">
|
||||
<div id="page-title">
|
||||
<h2>{% trans "Schedule V2 Backup" %} - <a target="_blank" href="http://go.cyberpanel.net/backup"
|
||||
style="height: 23px;line-height: 21px;"
|
||||
class="btn btn-border btn-alt border-red btn-link font-red"
|
||||
title=""><span>{% trans "Backup Docs" %}</span></a></h2>
|
||||
<p>{% trans "This page can be used to schedule your backups." %}</p>
|
||||
</div>
|
||||
|
||||
<div ng-controller="ScheduleV2Backup" class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Schedule v2 Backup" %} <img ng-hide="backupLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
<form class="form-horizontal bordered-row">
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Website" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select id="create-backup-select" ng-change="selectwebsite()" ng-model="selwebsite"
|
||||
class="form-control">
|
||||
{% for items in websiteList %}
|
||||
<option value="{{ items }}">{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Backup Frequency" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select id="create-backup-select" ng-model="frequency"
|
||||
class="form-control">
|
||||
<option>30 Minutes</option>
|
||||
<option>1 Hour</option>
|
||||
<option>6 Hours</option>
|
||||
<option>12 Hours</option>
|
||||
<option>1 Day</option>
|
||||
<option>3 Days</option>
|
||||
<option>1 Week</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Repo" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select id="reposelectbox" ng-change="selectrepo()" ng-model="testhabbi"
|
||||
class="form-control">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Backup Retention" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select id="create-backup-select" ng-model="retention"
|
||||
class="form-control">
|
||||
<option value="1">1 Day</option>
|
||||
<option value="3">3 Days</option>
|
||||
<option value="30">30 Days</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Backup Content" %}</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="websiteData" type="checkbox" value="">
|
||||
Data
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="websiteDatabases" type="checkbox" value="">
|
||||
Databases
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="websiteEmails" type="checkbox" value="">
|
||||
Emails
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!---
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="websiteSSLs" type="checkbox" value="">
|
||||
SSL Certificates
|
||||
</label>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans " " %} </label>
|
||||
<div class="col-sm-6">
|
||||
<button class="btn" id="CreateV2BackupButton" ng-click="CreateScheduleV2()"
|
||||
style="border-radius: 6px;background-color: #3447b7;color: white !important;">
|
||||
Create Schedule
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!------ List of records --------------->
|
||||
|
||||
<div class="form-group">
|
||||
|
||||
<div class="col-sm-12">
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Repo" %}</th>
|
||||
<th>{% trans "Frequency" %}</th>
|
||||
<th>{% trans "Retention" %}</th>
|
||||
<th>{% trans "Backup Websites?" %}</th>
|
||||
<th>{% trans "Backup Databases?" %}</th>
|
||||
<th>{% trans "Backup Emails?" %}</th>
|
||||
<th>{% trans "Last Run" %}</th>
|
||||
<th>{% trans "Delete" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="record in records track by $index">
|
||||
<td ng-bind="record.repo"></td>
|
||||
<td ng-bind="record.frequency"></td>
|
||||
<td ng-bind="record.retention"></td>
|
||||
<td ng-bind="record.websiteData"></td>
|
||||
<td ng-bind="record.websiteDatabases"></td>
|
||||
<td ng-bind="record.websiteEmails"></td>
|
||||
<td ng-bind="record.lastRun"></td>
|
||||
<a data-toggle="modal" href="" data-target="#DeleteScheduleV2">
|
||||
<td data-toggle="modal" data-target="#DeleteScheduleV2"
|
||||
ng-click="deleteBackupInitialv2(record.repo, record.frequency, record.websiteData, record.websiteDatabases, record.websiteEmails)">
|
||||
<img
|
||||
src="{% static 'images/delete.png' %}"></td>
|
||||
</a>
|
||||
<div id="DeleteScheduleV2" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">
|
||||
×
|
||||
</button>
|
||||
<h4 class="modal-title">{% trans "Set up account" %}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<form name="containerSettingsForm" action="/"
|
||||
class="form-horizontal">
|
||||
<div ng-hide="installationDetailsForm"
|
||||
class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Are you sure?" %}</label>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary"
|
||||
ng-click="DeleteScheduleV2()">Delete <img
|
||||
ng-hide="backupLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-default" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!------ List of records --------------->
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
{% else %}
|
||||
<style>
|
||||
.feturecard {
|
||||
width: 30%;
|
||||
padding: 46px;
|
||||
margin: 15px
|
||||
}
|
||||
</style>
|
||||
<div class="container">
|
||||
<div id="page-title" align="center">
|
||||
<h1 style="color: black"><strong>Backups v2 - Incremental Backups! </strong></h1>
|
||||
<p style="font-size: 15px; color: black; margin-top: 1%">Is your website's data protection strategy up
|
||||
to par? Are you tired of dealing with slow and unreliable backup solutions that don't offer the
|
||||
level of robustness you need? <br><br>
|
||||
|
||||
Introducing CyberPanel's latest feature, Backups v2! With Backups v2, you can ensure that your
|
||||
website's data is protected like never before. Our advanced incremental backup system utilizes
|
||||
rclone, a powerful backup tool that supports over 30+ backends, including popular cloud storage
|
||||
providers like Google Drive, Dropbox, Amazon S3, and more!<br><br>
|
||||
|
||||
Gone are the days of slow and cumbersome full backups. With Backups v2, you can take advantage of
|
||||
incremental backups that only capture changes to your data, making the process lightning-fast and
|
||||
highly efficient. This means you can save time and bandwidth while still maintaining the highest
|
||||
level of data integrity.<br><br>
|
||||
|
||||
Whether you have a small blog or a large e-commerce website, Backups v2 is tailored to meet your
|
||||
needs. With its robustness and versatility, you can have peace of mind knowing that your website's
|
||||
data is backed up securely and can be easily restored whenever you need it.
|
||||
<br></p>
|
||||
</div>
|
||||
<p align="center">
|
||||
<iframe width="788.54" height="443" src="https://www.youtube.com/embed/7dI1013xvUc"
|
||||
title="YouTube video player" frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen></iframe>
|
||||
</p>
|
||||
<div style="margin-top: 2%">
|
||||
<stripe-pricing-table pricing-table-id="prctbl_1PhTRPRs6rG0dTDlJZMzTw0k"
|
||||
publishable-key="pk_live_51PgodkRs6rG0dTDl3jCUgxjyjI983GmCdHjzuLfZIsssmDLgKgnXnwQghVCctKMNFIC5K4oMcviTFrHf1ytsYZGa00AGySiWlF">
|
||||
</stripe-pricing-table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% endblock %}
|
||||
393
IncBackups/templates/IncBackups/backupSchedule.html
Normal file
393
IncBackups/templates/IncBackups/backupSchedule.html
Normal file
@@ -0,0 +1,393 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Schedule Backup - 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 "Schedule Backup" %} - <a target="_blank" href="https://cyberpanel.net/"
|
||||
style="height: 23px;line-height: 21px;"
|
||||
class="btn btn-border btn-alt border-red btn-link font-red"
|
||||
title=""><span>{% trans "Remote Backups" %}</span></a></h2>
|
||||
<p>{% trans "On this page you can schedule Backups to localhost or remote server (If you have added one)." %}</p>
|
||||
</div>
|
||||
|
||||
<div ng-controller="scheduleBackupInc" class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Schedule Backup" %} <img ng-hide="cyberpanelLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
|
||||
<form action="/" class="form-horizontal bordered-row">
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Destination" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select ng-change="scheduleFreqView()" ng-model="backupDest" class="form-control">
|
||||
{% for items in destinations %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="scheduleFreq" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Frequency" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select ng-change="scheduleBtnView()" ng-model="backupFreq" class="form-control">
|
||||
<option>Daily</option>
|
||||
<option>Weekly</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="scheduleRetention" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Backup Retention. Leave 0 for no limit" %}</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="number">
|
||||
<label>
|
||||
<input ng-model="backupRetention" type="number" value="0">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div ng-hide="scheduleFreq" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Backup Content" %}</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="websiteData" type="checkbox" value="">
|
||||
Data
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="websiteDatabases" type="checkbox" value="">
|
||||
Databases
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="websiteEmails" type="checkbox" value="">
|
||||
Emails
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!---
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="websiteSSLs" type="checkbox" value="">
|
||||
SSL Certificates
|
||||
</label>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
|
||||
<div ng-hide="scheduleFreq" class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-click="addSchedule()"
|
||||
class="btn btn-primary btn-lg btn-block">{% trans "Add Destination" %}</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!------ List of Destinations --------------->
|
||||
|
||||
<!------ List of Accounts --------------->
|
||||
|
||||
<div ng-hide="scheduleFreq" class="form-group">
|
||||
|
||||
<div class="col-sm-12">
|
||||
<input type="text" ng-model="accountsSearch"
|
||||
placeholder="{% trans 'Search Accounts..' %}"
|
||||
class="form-control autocomplete-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="scheduleFreq" class="form-group">
|
||||
|
||||
<div class="col-sm-12">
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Select sites to be included in this job" %}</th>
|
||||
<th><input ng-model="webSiteStatus" ng-change="allChecked(webSiteStatus)"
|
||||
type="checkbox" value=""></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="web in WebSitesList | filter:accountsSearch">
|
||||
<td ng-bind="web.domain"></td>
|
||||
<td ng-click=""><input ng-model="webSiteStatus"
|
||||
ng-change="addRemoveWebsite(web.domain,webSiteStatus)"
|
||||
type="checkbox" value=""></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-4 col-sm-offset-8">
|
||||
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
|
||||
<li ng-repeat="page in pagination"
|
||||
ng-click="getFurtherWebsitesFromDB($index+1)" id="webPages"><a
|
||||
href="">{$ $index + 1 $}</a></li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!------ List of Accounts --------------->
|
||||
|
||||
<div class="form-group">
|
||||
|
||||
<div class="col-sm-12">
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "ID" %}</th>
|
||||
<th>{% trans "Destination" %}</th>
|
||||
<th>{% trans "Frequency" %}</th>
|
||||
<th>{% trans "Sites" %}</th>
|
||||
<th>{% trans "Delete" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="record in records track by $index">
|
||||
<td ng-bind="record.id"></td>
|
||||
<td ng-bind="record.destination"></td>
|
||||
<td ng-bind="record.frequency"></td>
|
||||
<td ng-bind="record.numberOfSites"></td>
|
||||
<td>
|
||||
<a ng-click="delSchedule(record.id)"
|
||||
class="btn btn-border btn-alt border-red btn-link font-red"
|
||||
href="#"
|
||||
title=""><span>{% trans 'Delete' %}</span></a>
|
||||
<a data-toggle="modal" data-target="#settings"
|
||||
ng-click="editInitial(record.id)"
|
||||
class="btn btn-border btn-alt border-purple btn-link font-purple"
|
||||
href="#"
|
||||
title=""><span>{% trans 'Edit' %}</span></a>
|
||||
|
||||
<!--- Modal --->
|
||||
<div id="settings" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">
|
||||
×
|
||||
</button>
|
||||
<h4 class="modal-title">Edit Job
|
||||
<img ng-hide="cyberpanelLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<form name="containerSettingsForm" action="/"
|
||||
class="form-horizontal">
|
||||
|
||||
<div ng-hide="installationDetailsForm"
|
||||
class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Job ID" %}</label>
|
||||
<div class="col-sm-4">
|
||||
<input name="name" type="number"
|
||||
class="form-control"
|
||||
ng-model="jobID" readonly>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationDetailsForm"
|
||||
class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Data" %}</label>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="$parent.websiteData"
|
||||
type="checkbox" value="">
|
||||
Data
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationDetailsForm"
|
||||
class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Databases" %}</label>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="$parent.websiteDatabases"
|
||||
type="checkbox" value="">
|
||||
Databases
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationDetailsForm"
|
||||
class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Emails" %}</label>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="$parent.websiteEmails"
|
||||
type="checkbox" value="">
|
||||
Emails
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="installationDetailsForm"
|
||||
class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "" %}</label>
|
||||
<button type="button" ng-click="saveChanges()"
|
||||
class="btn btn-primary btn-lg">{% trans "Save Changes" %}</button>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<ul class="nav nav-tabs">
|
||||
|
||||
<li class="col-md-3 nav-item tab-mod active">
|
||||
<a href="#tab-example-1" data-toggle="tab"
|
||||
class="h4 nav-link">
|
||||
<span>{% trans "Sites" %}</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
<li class="col-md-4 tab-mod nav-item">
|
||||
<a href="#tab-example-3" data-toggle="tab"
|
||||
class="h4 nav-link">
|
||||
<span>{% trans "Add Sites" %}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade active in"
|
||||
id="tab-example-1">
|
||||
<div class="example-box-wrapper">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "ID" %}</th>
|
||||
<th>{% trans "Website" %}</th>
|
||||
<th>{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="record in websites track by $index">
|
||||
<td ng-bind="record.id"></td>
|
||||
<td ng-bind="record.website"></td>
|
||||
<td>
|
||||
<a ng-click="removeSite(record.website)"
|
||||
class="btn btn-border btn-alt border-red btn-link font-red"
|
||||
href="#"
|
||||
title=""><span>{% trans 'Delete' %}</span></a>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab-pane fade" id="tab-example-3">
|
||||
|
||||
<form action="/"
|
||||
class="form-horizontal bordered-row">
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Site" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select ng-model="$parent.websiteToBeAdded"
|
||||
class="form-control">
|
||||
{% for items in websiteList %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<img ng-hide="cyberpanelLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</div>
|
||||
|
||||
<div ng-hide="savebtn"
|
||||
class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button"
|
||||
ng-click="addWebsite()"
|
||||
class="btn btn-primary btn-lg btn-block">{% trans "Add" %}</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--- Modal End--->
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!------ List of records --------------->
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
593
IncBackups/templates/IncBackups/createBackup.html
Normal file
593
IncBackups/templates/IncBackups/createBackup.html
Normal file
@@ -0,0 +1,593 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Create Incremental Backup" %}{% endblock %}
|
||||
|
||||
{% block header_scripts %}
|
||||
<style>
|
||||
/* Page Specific Styles */
|
||||
.backup-wrapper {
|
||||
background: transparent;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.backup-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Page Header */
|
||||
.page-header {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 25px;
|
||||
margin-bottom: 25px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||
border: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #2f3640;
|
||||
margin: 0 0 10px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.page-header .icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: #5b5fcf;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
box-shadow: 0 4px 12px rgba(91,95,207,0.3);
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
font-size: 14px;
|
||||
color: #64748b;
|
||||
margin: 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.docs-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: #5b5fcf;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.docs-link:hover {
|
||||
color: #4b4fbf;
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
/* Content Section */
|
||||
.content-section {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
margin-bottom: 25px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||
border: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #2f3640;
|
||||
margin-bottom: 25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.section-title::before {
|
||||
content: '';
|
||||
width: 4px;
|
||||
height: 24px;
|
||||
background: #5b5fcf;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* Form Styles */
|
||||
.form-group {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #2f3640;
|
||||
margin-bottom: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 8px;
|
||||
background: #f8f9ff;
|
||||
color: #2f3640;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: #5b5fcf;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgba(91,95,207,0.1);
|
||||
}
|
||||
|
||||
.form-select {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 8px;
|
||||
background: #f8f9ff;
|
||||
color: #2f3640;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-select:focus {
|
||||
outline: none;
|
||||
border-color: #5b5fcf;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgba(91,95,207,0.1);
|
||||
}
|
||||
|
||||
/* Checkbox Styles */
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.checkbox-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
background: #f8f9ff;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.checkbox-wrapper:hover {
|
||||
background: #f0f0ff;
|
||||
border-color: #5b5fcf;
|
||||
}
|
||||
|
||||
.checkbox-wrapper input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
font-size: 14px;
|
||||
color: #2f3640;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Button Styles */
|
||||
.btn-primary {
|
||||
background: #5b5fcf;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 30px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #4b4fbf;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(91,95,207,0.3);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
background: #94a3b8;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Status Textarea */
|
||||
.status-textarea {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
font-size: 13px;
|
||||
font-family: 'Monaco', 'Consolas', monospace;
|
||||
line-height: 1.5;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 8px;
|
||||
background: #f8f9ff;
|
||||
color: #2f3640;
|
||||
resize: vertical;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
/* Table Styles */
|
||||
.backups-table {
|
||||
width: 100%;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.backups-table th {
|
||||
background: #f8f9ff;
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #64748b;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.backups-table td {
|
||||
padding: 15px;
|
||||
font-size: 14px;
|
||||
color: #2f3640;
|
||||
border-bottom: 1px solid #f0f0ff;
|
||||
}
|
||||
|
||||
.backups-table tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.backups-table tr:hover {
|
||||
background: #f8f9ff;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
background: #5b5fcf;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
background: #4b4fbf;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(91,95,207,0.3);
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.delete-btn:hover {
|
||||
background: #dc2626;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(239,68,68,0.3);
|
||||
}
|
||||
|
||||
/* Modal Styles */
|
||||
.modal-content {
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
background: #f8f9ff;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
border-radius: 12px 12px 0 0;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #2f3640;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
background: #f8f9ff;
|
||||
border-top: 1px solid #e8e9ff;
|
||||
border-radius: 0 0 12px 12px;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
/* Loading Spinner */
|
||||
.loading-spinner {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid #5b5fcf;
|
||||
border-radius: 50%;
|
||||
border-top-color: transparent;
|
||||
animation: spin 0.8s linear infinite;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
/* Angular cloak */
|
||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Empty State */
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #8893a7;
|
||||
}
|
||||
|
||||
.empty-state i {
|
||||
font-size: 48px;
|
||||
color: #e8e9ff;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.empty-state p {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.backup-wrapper {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.content-section {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.backups-table {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.backups-table th,
|
||||
.backups-table td {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% load static %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
|
||||
<div class="backup-wrapper">
|
||||
<div class="backup-container" ng-controller="createIncrementalBackups" ng-init="cyberpanelLoading=false" ng-cloak>
|
||||
<!-- Page Header -->
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
<div class="icon">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
</div>
|
||||
{% trans "Create Incremental Backup" %}
|
||||
</h1>
|
||||
<p>{% trans "Create incremental backups for your websites with efficient storage usage and quick restore capabilities." %}</p>
|
||||
<a href="https://cyberpanel.net/docs/2-create-restore-incremental-backups/" target="_blank" class="docs-link">
|
||||
<i class="fas fa-book"></i>
|
||||
{% trans "View Documentation" %}
|
||||
<i class="fas fa-external-link-alt" style="font-size: 11px;"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Configuration Section -->
|
||||
<div class="content-section">
|
||||
<h2 class="section-title">{% trans "Backup Configuration" %}</h2>
|
||||
|
||||
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label class="form-label">{% trans "Select Website" %}</label>
|
||||
<select ng-change="fetchDetails()" ng-model="websiteToBeBacked" class="form-select">
|
||||
<option value="">{% trans "Choose a website to backup" %}</option>
|
||||
{% for items in websiteList %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div ng-hide="destination" class="form-group">
|
||||
<label class="form-label">{% trans "Backup Destination" %}</label>
|
||||
<select ng-change="destinationSelection()" ng-model="backupDestinations" class="form-select">
|
||||
<option value="">{% trans "Select backup destination" %}</option>
|
||||
{% for items in destinations %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div ng-hide="destination" class="form-group">
|
||||
<label class="form-label">{% trans "Backup Content" %}</label>
|
||||
<div class="checkbox-group">
|
||||
<div class="checkbox-wrapper">
|
||||
<input ng-model="websiteData" type="checkbox" id="backup-data" checked>
|
||||
<label for="backup-data" class="checkbox-label">{% trans "Website Data" %}</label>
|
||||
</div>
|
||||
<div class="checkbox-wrapper">
|
||||
<input ng-model="websiteDatabases" type="checkbox" id="backup-databases">
|
||||
<label for="backup-databases" class="checkbox-label">{% trans "Databases" %}</label>
|
||||
</div>
|
||||
<div class="checkbox-wrapper">
|
||||
<input ng-model="websiteEmails" type="checkbox" id="backup-emails">
|
||||
<label for="backup-emails" class="checkbox-label">{% trans "Email Accounts" %}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Backup Status -->
|
||||
<div ng-hide="runningBackup" class="form-group">
|
||||
<label class="form-label">{% trans "Backup Progress" %}</label>
|
||||
<textarea ng-model="status" class="status-textarea" readonly></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Create Backup Button -->
|
||||
<div ng-hide="backupButton" class="form-group" style="text-align: center; margin-top: 30px;">
|
||||
<button type="button" ng-click="createBackup()" class="btn-primary" ng-disabled="cyberpanelLoading">
|
||||
<i class="fas fa-shield-alt" ng-if="!cyberpanelLoading"></i>
|
||||
<i class="fas fa-spinner fa-spin" ng-if="cyberpanelLoading"></i>
|
||||
<span ng-if="!cyberpanelLoading">{% trans "Create Backup" %}</span>
|
||||
<span ng-if="cyberpanelLoading">{% trans "Creating Backup..." %}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Existing Backups -->
|
||||
<div class="content-section">
|
||||
<h2 class="section-title">{% trans "Existing Backups" %}</h2>
|
||||
|
||||
<div ng-if="!records || records.length === 0" class="empty-state">
|
||||
<i class="fas fa-folder-open"></i>
|
||||
<p>{% trans "No backups created yet" %}</p>
|
||||
</div>
|
||||
|
||||
<table class="backups-table" ng-if="records && records.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Backup ID" %}</th>
|
||||
<th>{% trans "Date Created" %}</th>
|
||||
<th style="text-align: center;">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="record in records track by $index">
|
||||
<td>
|
||||
<i class="fas fa-archive" style="color: #5b5fcf; margin-right: 8px;"></i>
|
||||
<span ng-bind="record.id"></span>
|
||||
</td>
|
||||
<td ng-bind="record.date"></td>
|
||||
<td style="text-align: center;">
|
||||
<a ng-click="restore(record.id)" data-toggle="modal" data-target="#settings"
|
||||
class="action-btn" title="Restore">
|
||||
<i class="fas fa-undo"></i>
|
||||
{% trans "Restore Points" %}
|
||||
</a>
|
||||
<button type="button" class="delete-btn" ng-click="deleteBackup(record.id)" style="margin-left: 10px;">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
{% trans "Delete" %}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Restore Points Modal -->
|
||||
<div id="settings" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">
|
||||
{% trans "Restore Points" %}
|
||||
<span class="loading-spinner" ng-show="cyberpanelLoading"></span>
|
||||
</h4>
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table class="backups-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Job ID" %}</th>
|
||||
<th>{% trans "Snapshot ID" %}</th>
|
||||
<th>{% trans "Type" %}</th>
|
||||
<th>{% trans "Destination" %}</th>
|
||||
<th style="text-align: center;">{% trans "Action" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="job in jobs track by $index">
|
||||
<td ng-bind="job.id"></td>
|
||||
<td ng-bind="job.snapshotid"></td>
|
||||
<td ng-bind="job.type"></td>
|
||||
<td ng-bind="job.destination"></td>
|
||||
<td style="text-align: center;">
|
||||
<button ng-click="restorePoint(job.id, 0)" class="action-btn">
|
||||
<i class="fas fa-undo"></i>
|
||||
{% trans "Restore" %}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div ng-hide="restoreSt" style="margin-top: 20px;">
|
||||
<label class="form-label">{% trans "Restore Progress" %}</label>
|
||||
<textarea ng-model="status" class="status-textarea" rows="7" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">
|
||||
{% trans "Close" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="{% static 'IncBackups/IncBackups.js' %}"></script>
|
||||
{% endblock %}
|
||||
503
IncBackups/templates/IncBackups/incrementalDestinations.html
Normal file
503
IncBackups/templates/IncBackups/incrementalDestinations.html
Normal file
@@ -0,0 +1,503 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Set up Backup Destinations" %}{% endblock %}
|
||||
|
||||
{% block header_scripts %}
|
||||
<style>
|
||||
/* Page Specific Styles */
|
||||
.backup-wrapper {
|
||||
background: transparent;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.backup-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Page Header */
|
||||
.page-header {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 25px;
|
||||
margin-bottom: 25px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||
border: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #2f3640;
|
||||
margin: 0 0 10px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.page-header .icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: #5b5fcf;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
box-shadow: 0 4px 12px rgba(91,95,207,0.3);
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
font-size: 14px;
|
||||
color: #64748b;
|
||||
margin: 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.docs-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: #5b5fcf;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.docs-link:hover {
|
||||
color: #4b4fbf;
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
/* Content Section */
|
||||
.content-section {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
margin-bottom: 25px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||
border: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #2f3640;
|
||||
margin-bottom: 25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.section-title::before {
|
||||
content: '';
|
||||
width: 4px;
|
||||
height: 24px;
|
||||
background: #5b5fcf;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* Form Styles */
|
||||
.form-group {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #2f3640;
|
||||
margin-bottom: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 8px;
|
||||
background: #f8f9ff;
|
||||
color: #2f3640;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: #5b5fcf;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgba(91,95,207,0.1);
|
||||
}
|
||||
|
||||
.form-select {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 8px;
|
||||
background: #f8f9ff;
|
||||
color: #2f3640;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-select:focus {
|
||||
outline: none;
|
||||
border-color: #5b5fcf;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgba(91,95,207,0.1);
|
||||
}
|
||||
|
||||
.form-help {
|
||||
font-size: 12px;
|
||||
color: #8893a7;
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.form-help i {
|
||||
font-size: 13px;
|
||||
color: #5b5fcf;
|
||||
}
|
||||
|
||||
/* Button Styles */
|
||||
.btn-primary {
|
||||
background: #5b5fcf;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 30px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #4b4fbf;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(91,95,207,0.3);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
background: #94a3b8;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Angular cloak */
|
||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Loading Spinner */
|
||||
.loading-spinner {
|
||||
display: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid #ffffff;
|
||||
border-radius: 50%;
|
||||
border-top-color: transparent;
|
||||
animation: spin 0.8s linear infinite;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.loading-spinner.ng-hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Show spinner only when Angular explicitly shows it */
|
||||
.loading-spinner[style*="display: none;"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.loading-spinner.ng-hide-add,
|
||||
.loading-spinner.ng-hide-remove {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.btn-primary .loading-spinner:not([style*="display: none;"]):not(.ng-hide) {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Table Styles */
|
||||
.destinations-table {
|
||||
width: 100%;
|
||||
margin-top: 30px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.destinations-table th {
|
||||
background: #f8f9ff;
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #64748b;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.destinations-table td {
|
||||
padding: 15px;
|
||||
font-size: 14px;
|
||||
color: #2f3640;
|
||||
border-bottom: 1px solid #f0f0ff;
|
||||
}
|
||||
|
||||
.destinations-table tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.destinations-table tr:hover {
|
||||
background: #f8f9ff;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.delete-btn:hover {
|
||||
background: #dc2626;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(239,68,68,0.3);
|
||||
}
|
||||
|
||||
/* Empty State */
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #8893a7;
|
||||
}
|
||||
|
||||
.empty-state i {
|
||||
font-size: 48px;
|
||||
color: #e8e9ff;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.empty-state p {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Alert Box */
|
||||
.alert-box {
|
||||
background: #f0f0ff;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.alert-box i {
|
||||
color: #5b5fcf;
|
||||
font-size: 18px;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.alert-box-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.alert-box-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #2f3640;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.alert-box-text {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.backup-wrapper {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.content-section {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.destinations-table {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.destinations-table th,
|
||||
.destinations-table td {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% load static %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
|
||||
<div class="backup-wrapper">
|
||||
<div class="backup-container" ng-controller="incrementalDestinations">
|
||||
<!-- Page Header -->
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
<div class="icon">
|
||||
<i class="fas fa-cloud-upload-alt"></i>
|
||||
</div>
|
||||
{% trans "Set up Incremental Backup Destinations" %}
|
||||
</h1>
|
||||
<p>{% trans "Configure your backup destinations for incremental backups. Currently supporting AWS S3 storage." %}</p>
|
||||
<a href="https://cyberpanel.net/docs/1-add-remove-destinations-for-incremental-backups/" target="_blank" class="docs-link">
|
||||
<i class="fas fa-book"></i>
|
||||
{% trans "View Documentation" %}
|
||||
<i class="fas fa-external-link-alt" style="font-size: 11px;"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Configuration Section -->
|
||||
<div class="content-section">
|
||||
<h2 class="section-title">{% trans "Add Backup Destination" %}</h2>
|
||||
|
||||
<div class="alert-box">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<div class="alert-box-content">
|
||||
<div class="alert-box-title">{% trans "Important Information" %}</div>
|
||||
<div class="alert-box-text">{% trans "Incremental backups allow you to save storage space and bandwidth by only backing up changes since the last backup." %}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label class="form-label">{% trans "Destination Type" %}</label>
|
||||
<select ng-change="fetchDetails()" ng-model="destinationType" class="form-select">
|
||||
<option value="">{% trans "Select destination type" %}</option>
|
||||
<option value="AWS">AWS S3</option>
|
||||
</select>
|
||||
<div class="form-help">
|
||||
<i class="fas fa-lightbulb"></i>
|
||||
{% trans "Choose your backup storage provider" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AWS Configuration -->
|
||||
<div ng-hide="awsHide">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{% trans "AWS Access Key ID" %}</label>
|
||||
<input type="text" class="form-control" ng-model="AWS_ACCESS_KEY_ID" placeholder="{% trans 'Enter your AWS access key' %}" required>
|
||||
<div class="form-help">
|
||||
<i class="fas fa-key"></i>
|
||||
{% trans "You can find this in your AWS IAM console" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">{% trans "AWS Secret Access Key" %}</label>
|
||||
<input type="password" class="form-control" ng-model="AWS_SECRET_ACCESS_KEY" placeholder="{% trans 'Enter your AWS secret key' %}" required>
|
||||
<div class="form-help">
|
||||
<i class="fas fa-lock"></i>
|
||||
{% trans "Keep this key secure and never share it" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" ng-click="addDestination('AWS')" class="btn-primary" ng-disabled="cyberpanelLoading">
|
||||
<i class="fas fa-plus-circle" ng-hide="cyberpanelLoading"></i>
|
||||
<i class="fas fa-spinner fa-spin" ng-show="cyberpanelLoading" ng-cloak></i>
|
||||
<span ng-hide="cyberpanelLoading">{% trans "Add Destination" %}</span>
|
||||
<span ng-show="cyberpanelLoading" ng-cloak>{% trans "Adding..." %}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Existing Destinations -->
|
||||
<div class="content-section" ng-hide="awsHide">
|
||||
<h2 class="section-title">{% trans "Configured Destinations" %}</h2>
|
||||
|
||||
<div ng-if="!records || records.length === 0" class="empty-state">
|
||||
<i class="fas fa-cloud-slash"></i>
|
||||
<p>{% trans "No backup destinations configured yet" %}</p>
|
||||
</div>
|
||||
|
||||
<table class="destinations-table" ng-if="records && records.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "AWS Access Key ID" %}</th>
|
||||
<th style="text-align: right;">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="record in records track by $index">
|
||||
<td>
|
||||
<i class="fas fa-key" style="color: #5b5fcf; margin-right: 8px;"></i>
|
||||
<span ng-bind="record.AWS_ACCESS_KEY_ID"></span>
|
||||
</td>
|
||||
<td style="text-align: right;">
|
||||
<button type="button" class="delete-btn" ng-click="removeDestination('AWS', record.AWS_ACCESS_KEY_ID)">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
{% trans "Delete" %}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="{% static 'IncBackups/IncBackups.js' %}"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Initialize the page with AWS selected by default
|
||||
setTimeout(function() {
|
||||
var scope = angular.element(document.querySelector('[ng-controller="incrementalDestinations"]')).scope();
|
||||
if (scope && scope.fetchDetails) {
|
||||
scope.$apply(function() {
|
||||
scope.destinationType = 'AWS';
|
||||
scope.fetchDetails();
|
||||
});
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
135
IncBackups/templates/IncBackups/restoreRemoteBackups.html
Normal file
135
IncBackups/templates/IncBackups/restoreRemoteBackups.html
Normal file
@@ -0,0 +1,135 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Restore Remote Incremental Backups" %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
<div class="container">
|
||||
<div id="page-title">
|
||||
<h2>{% trans "Restore Remote Incremental Backups" %} - <a target="_blank"
|
||||
href="https://cyberpanel.net/docs/3-restore-backups-from-remote-destination/"
|
||||
style="height: 23px;line-height: 21px;"
|
||||
class="btn btn-border btn-alt border-red btn-link font-red"
|
||||
title=""><span>{% trans "Backup Docs" %}</span></a>
|
||||
</h2>
|
||||
<p>{% trans "This page can be used to restore remote incremental backups for your websites." %}</p>
|
||||
</div>
|
||||
|
||||
<div ng-controller="restoreRemoteBackupsInc" class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Backup Website" %} <img ng-hide="cyberpanelLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
|
||||
<form action="/" class="form-horizontal bordered-row">
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Website" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select ng-change="showThings()" ng-model="websiteToBeBacked" class="form-control">
|
||||
{% for items in websiteList %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="destination" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Destination" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select ng-model="backupDestinations"
|
||||
class="form-control">
|
||||
{% for items in destinations %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="destination" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Encrypted Backup Password" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="password" name="password" class="form-control" ng-model="password"
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="destination" class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-click="fetchDetails()"
|
||||
class="btn btn-primary btn-lg btn-block">{% trans "Fetch Restore Points" %} <img
|
||||
ng-hide="cyberpanelLoading"
|
||||
src="{% static 'images/loading.gif' %}"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!---- if Backup is running ----->
|
||||
|
||||
<div ng-hide="runningBackup" class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<div class="col-sm-12">
|
||||
<textarea ng-model="status" class="form-control" rows="10"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!---- if Backup is running------>
|
||||
|
||||
<!------ List of records --------------->
|
||||
|
||||
<div class="form-group">
|
||||
|
||||
<div class="col-sm-12">
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Snapshot ID" %}</th>
|
||||
<th>{% trans "Date" %}</th>
|
||||
<th>{% trans "Host" %}</th>
|
||||
<th>{% trans "Path" %}</th>
|
||||
<th>{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="record in records track by $index">
|
||||
<td ng-bind="record.id"></td>
|
||||
<td ng-bind="record.date"></td>
|
||||
<td ng-bind="record.host"></td>
|
||||
<td ng-bind="record.path"></td>
|
||||
<td>
|
||||
<a ng-click="restorePoint(record.id, record.path)"
|
||||
class="btn btn-border btn-alt border-green btn-link font-green"
|
||||
title=""><span>Restore</span></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!------ List of records --------------->
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
35
IncBackups/tests.py
Normal file
35
IncBackups/tests.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import json
|
||||
import configparser
|
||||
|
||||
CurrentContent = """[usman]
|
||||
type = sftp
|
||||
host = staging.cyberpanel.net
|
||||
user = abcds2751
|
||||
pass = s0RBbJU8EhfQ-wvFgbOVEmy3HK6y19A
|
||||
shell_type = unix
|
||||
md5sum_command = md5sum
|
||||
sha1sum_command = sha1sum
|
||||
|
||||
[habbitest2gdrive]
|
||||
type = drive
|
||||
client_id = ""
|
||||
client_secret = ""
|
||||
scope = drive
|
||||
root_folder_id = ""
|
||||
service_account_file = ""
|
||||
token = {"access_token":"ya29.a0Ael9sCPUpwAZpHChyBkAYrDGo5BRjkj2OV1r9KNBXdXZZrjTrjPOHxTkayEr-hfKNhsqYrvChowxQw-EgTO7JobBE7IrZpLDpdpEOTY49JOg-PagtPLU_TuqFPab356TdeC0-f2RHQ_2arU1pN92aKcgfp7CaCgYKASESARESFQF4udJhaS1_8FVFFkG-ds0yPY0APA0163","token_type":"Bearer","refresh_token":"1//09Sgboc4b9-kYCgYIARAAGAkSNgF-L9IrgxJ3jKcd0UDraNAncWDKRUNu0L5ORiaS8H_QaXv2y85p0cL3ZArEaShSxy2P_Kb0CQ"}
|
||||
"""
|
||||
|
||||
# Read the configuration string
|
||||
config = configparser.ConfigParser()
|
||||
config.read_string(CurrentContent)
|
||||
|
||||
# Get the refresh token
|
||||
refresh_token = json.loads(config.get('habbitest2gdrive', 'token'))['refresh_token']
|
||||
old_access_token = json.loads(config.get('habbitest2gdrive', 'token'))['access_token']
|
||||
print(refresh_token)
|
||||
|
||||
new_token ="jdskjkvnckjdfvnjknvkvdjc"
|
||||
new_string = CurrentContent.replace(str(old_access_token), new_token)
|
||||
|
||||
print(new_string)
|
||||
45
IncBackups/urls.py
Normal file
45
IncBackups/urls.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('createBackup', views.create_backup, name='createBackupInc'),
|
||||
path('restoreRemoteBackups', views.restore_remote_backups, name='restoreRemoteBackupsInc'),
|
||||
path('backupDestinations', views.backup_destinations, name='backupDestinationsInc'),
|
||||
path('addDestination', views.add_destination, name='addDestinationInc'),
|
||||
path('populateCurrentRecords', views.populate_current_records, name='populateCurrentRecordsInc'),
|
||||
path('removeDestination', views.remove_destination, name='removeDestinationInc'),
|
||||
path('fetchCurrentBackups', views.fetch_current_backups, name='fetchCurrentBackupsInc'),
|
||||
path('submitBackupCreation', views.submit_backup_creation, name='submitBackupCreationInc'),
|
||||
path('getBackupStatus', views.get_backup_status, name='getBackupStatusInc'),
|
||||
path('deleteBackup', views.delete_backup, name='deleteBackupInc'),
|
||||
path('fetchRestorePoints', views.fetch_restore_points, name='fetchRestorePointsInc'),
|
||||
path('restorePoint', views.restore_point, name='restorePointInc'),
|
||||
path('scheduleBackups', views.schedule_backups, name='scheduleBackupsInc'),
|
||||
path('submitBackupSchedule', views.submit_backup_schedule, name='submitBackupScheduleInc'),
|
||||
path('scheduleDelete', views.schedule_delete, name='scheduleDeleteInc'),
|
||||
path('getCurrentBackupSchedules', views.get_current_backup_schedules, name='getCurrentBackupSchedulesInc'),
|
||||
path('fetchSites', views.fetch_sites, name='fetchSites'),
|
||||
path('saveChanges', views.save_changes, name='saveChanges'),
|
||||
path('removeSite', views.remove_site, name='removeSite'),
|
||||
path('addWebsite', views.add_website, name='addWebsite'),
|
||||
|
||||
# V2 Backups URLs
|
||||
path('CreateV2Backup', views.CreateV2Backup, name='CreateV2Backup'),
|
||||
path('ConfigureV2Backup', views.ConfigureV2Backup, name='ConfigureV2Backup'),
|
||||
path('ConfigureV2BackupSetup', views.ConfigureV2BackupSetup, name='ConfigureV2BackupSetup'),
|
||||
path('RestoreV2backupSite', views.RestoreV2backupSite, name='RestoreV2backupSite'),
|
||||
path('selectwebsiteRetorev2', views.selectwebsiteRetorev2, name='selectwebsiteRetorev2'),
|
||||
path('selectreporestorev2', views.selectreporestorev2, name='selectreporestorev2'),
|
||||
path('RestorePathV2', views.RestorePathV2, name='RestorePathV2'),
|
||||
path('DeleteSnapshotV2Final', views.DeleteSnapshotV2Final, name='DeleteSnapshotV2Final'),
|
||||
path('CreateV2BackupButton', views.CreateV2BackupButton, name='CreateV2BackupButton'),
|
||||
path('selectwebsiteCreatev2', views.selectwebsiteCreatev2, name='selectwebsiteCreatev2'),
|
||||
path('CreateV2BackupStatus', views.CreateV2BackupStatus, name='CreateV2BackupStatus'),
|
||||
path('ConfigureSftpV2Backup', views.ConfigureSftpV2Backup, name='ConfigureSftpV2Backup'),
|
||||
|
||||
path('schedulev2Backups', views.schedulev2Backups, name='schedulev2Backups'),
|
||||
path('DeleteScheduleV2', views.DeleteScheduleV2, name='DeleteScheduleV2'),
|
||||
path('CreateScheduleV2', views.CreateScheduleV2, name='CreateScheduleV2'),
|
||||
path('DeleteRepoV2', views.DeleteRepoV2, name='DeleteRepoV2'),
|
||||
path('DeleteV2BackupButton', views.DeleteV2BackupButton, name='DeleteV2BackupButton'),
|
||||
]
|
||||
1284
IncBackups/views.py
Normal file
1284
IncBackups/views.py
Normal file
File diff suppressed because it is too large
Load Diff
675
LICENSE
Executable file
675
LICENSE
Executable file
@@ -0,0 +1,675 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
||||
177
README.md
Executable file
177
README.md
Executable file
@@ -0,0 +1,177 @@
|
||||
# 🛠️ CyberPanel
|
||||
|
||||
Web Hosting Control Panel powered by OpenLiteSpeed, designed to simplify hosting management.
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Features & Services
|
||||
|
||||
- 🔐 **Different User Access Levels** (via ACLs).
|
||||
- 🌌 **Auto SSL** for secure websites.
|
||||
- 💻 **FTP Server** for file transfers.
|
||||
- 🕒 **Light-weight DNS Server** (PowerDNS).
|
||||
- 🔐 **phpMyAdmin** to manage databases (MariaDB).
|
||||
- 📧 **Email Support** (SnappyMail).
|
||||
- 🕌 **File Manager** for quick file access.
|
||||
- 🌐 **PHP Management** made easy.
|
||||
- 🔒 **Firewall** (✅ FirewallD & ConfigServer Firewall Integration).
|
||||
- 📀 **One-click Backups and Restores**.
|
||||
|
||||
---
|
||||
|
||||
## 🔢 Supported PHP Versions
|
||||
|
||||
CyberPanel supports PHP versions based on your operating system:
|
||||
|
||||
### ☑️ **PHP 8.0 and Above**
|
||||
- Fully supported on modern systems such as Ubuntu 22.04 and AlmaLinux 9.x and higher.
|
||||
|
||||
### ☑️ **PHP 7.4 and Below**
|
||||
- Compatible with AlmaLinux 8, Ubuntu 18.04, and similar environments.
|
||||
|
||||
### Adding PHP Versions as Third-Party Add-ons
|
||||
|
||||
Some PHP versions can be added to operating systems as third-party packages using external repositories or tools. Here's an overview by OS:
|
||||
|
||||
#### **Ubuntu**:
|
||||
- **Ubuntu 22.04**:
|
||||
- Highest: PHP 8.5 (default repository or Ondrej's PPA).
|
||||
- Lowest: PHP 7.4 (via Ondrej's PPA).
|
||||
- **Ubuntu 20.04**:
|
||||
- Highest: PHP 8.5 (default repository or Ondrej's PPA).
|
||||
- Lowest: PHP 7.0 (via Ondrej's PPA).
|
||||
- **Ubuntu 18.04**:
|
||||
- Highest: PHP 8.4 (via Ondrej's PPA).
|
||||
- Lowest: PHP 5.6 (via Ondrej's PPA).
|
||||
|
||||
#### **AlmaLinux**:
|
||||
- **AlmaLinux 9**:
|
||||
- Highest: PHP 8.5 (default repository or Remi repository).
|
||||
- Lowest: PHP 7.4 (via Remi repository).
|
||||
- **AlmaLinux 8**:
|
||||
- Highest: PHP 8.4 (default repository or Remi repository).
|
||||
- Lowest: PHP 5.6 (via Remi repository).
|
||||
|
||||
#### **CentOS**:
|
||||
- **CentOS 9**:
|
||||
- Highest: PHP 8.4 (via Remi repository).
|
||||
- Lowest: PHP 7.4 (via Remi repository).
|
||||
- **CentOS 8**:
|
||||
- Highest: PHP 8.4 (via Remi repository).
|
||||
- Lowest: PHP 5.6 (via Remi repository).
|
||||
- **CentOS 7**:
|
||||
- Highest: PHP 8.0 (via Remi repository).
|
||||
- Lowest: PHP 5.4 (via Remi repository).
|
||||
|
||||
#### **RHEL**:
|
||||
- **RHEL 9**:
|
||||
- Highest: PHP 8.4 (via Remi repository).
|
||||
- Lowest: PHP 7.4 (via Remi repository).
|
||||
- **RHEL 8**:
|
||||
- Highest: PHP 8.4 (via Remi repository).
|
||||
- Lowest: PHP 5.6 (via Remi repository).
|
||||
|
||||
#### **RockyLinux**:
|
||||
- **RockyLinux 8**:
|
||||
- Highest: PHP 8.5 (via Remi repository).
|
||||
- Lowest: PHP 5.6 (via Remi repository).
|
||||
|
||||
#### **CloudLinux**:
|
||||
- **CloudLinux 8**:
|
||||
- Highest: PHP 8.5 (via Remi repository).
|
||||
- Lowest: PHP 5.6 (via Remi repository).
|
||||
- **CloudLinux 7**:
|
||||
- Highest: PHP 8.0 (via Remi repository).
|
||||
- Lowest: PHP 5.4 (via Remi repository).
|
||||
|
||||
#### **openEuler**:
|
||||
- **openEuler 22.03**:
|
||||
- Highest: PHP 8.4 (default repository).
|
||||
- Lowest: PHP 7.4 (default repository).
|
||||
- **openEuler 20.03**:
|
||||
- Highest: PHP 7.3 (default repository).
|
||||
- Lowest: PHP 7.0 (default repository).
|
||||
|
||||
### Full List of PHP Versions and End of Life (EOL) Dates:
|
||||
- ✨ **PHP 8.5** - EOL: 31 Dec 2028.
|
||||
- ✨ **PHP 8.4** - EOL: 31 Dec 2027.
|
||||
- ✨ **PHP 8.3** - EOL: 31 Dec 2027.
|
||||
- ✨ **PHP 8.2** - EOL: 31 Dec 2026.
|
||||
- ✨ **PHP 8.1** - EOL: 31 Dec 2025.
|
||||
- 🛑 **PHP 8.0** - EOL: 26 Nov 2023.
|
||||
- 🛑 **PHP 7.4** - EOL: 28 Nov 2022.
|
||||
- 🛑 **PHP 7.3** - EOL: 6 Dec 2021.
|
||||
- 🛑 **PHP 7.2** - EOL: 30 Nov 2020.
|
||||
- 🛑 **PHP 7.1** - EOL: 1 Dec 2019.
|
||||
- 🛑 **PHP 7.0** - EOL: 10 Jan 2019.
|
||||
- 🛑 **PHP 5.6** - EOL: 31 Dec 2018.
|
||||
- 🛑 **PHP 5.5** - EOL: 21 Jul 2016.
|
||||
- 🛑 **PHP 5.4** - EOL: 3 Sep 2015.
|
||||
- 🛑 **PHP 5.3** - EOL: 14 Aug 2014.
|
||||
|
||||
---
|
||||
|
||||
## 🌐 Supported OS Versions
|
||||
|
||||
CyberPanel runs on x86_64 architecture and supports the following operating systems:
|
||||
|
||||
### **Ubuntu**:
|
||||
- Ubuntu 22.04 ✅ Supported until April 2027.
|
||||
- Ubuntu 20.04 ✅ Supported until April 2025.
|
||||
- Ubuntu 18.04 🛑 EOL: 31 May 2023.
|
||||
|
||||
### **CentOS**:
|
||||
- CentOS 9 ✅ EOL: 31 May 2027.
|
||||
- CentOS 8 🛑 EOL: 31 Dec 2021.
|
||||
- CentOS 7 🛑 EOL: 30 June 2024.
|
||||
|
||||
### **RHEL**:
|
||||
- RHEL 9 ✅ EOL: 31 May 2032.
|
||||
- RHEL 8 ✅ EOL: 31 May 2029.
|
||||
|
||||
### **AlmaLinux**:
|
||||
- AlmaLinux 9 ✅ EOL: 31 May 2032.
|
||||
- AlmaLinux 8 ✅ EOL: 31 May 2029.
|
||||
|
||||
### **Other OS**:
|
||||
- RockyLinux 9 ✅ EOL: 31 May 2032.
|
||||
- RockyLinux 8 ✅ EOL: 31 May 2029.
|
||||
- CloudLinux 8 ✅ EOL: 31 May 2029.
|
||||
- CloudLinux 7 🛑 EOL: 1 Jul 2024.
|
||||
- openEuler 22.03 🛑 EOL: March 2024.
|
||||
- openEuler 20.03 🛑 EOL: April 2022.
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Installation Instructions
|
||||
|
||||
Install CyberPanel easily with the following command:
|
||||
|
||||
```bash
|
||||
sh <(curl https://cyberpanel.net/install.sh || wget -O - https://cyberpanel.net/install.sh)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Upgrading CyberPanel
|
||||
|
||||
Upgrade your CyberPanel installation using:
|
||||
|
||||
```bash
|
||||
sh <(curl https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgrade.sh || wget -O - https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/preUpgrade.sh)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Resources
|
||||
|
||||
- 🌐 [Official Site](https://cyberpanel.net)
|
||||
- ✏️ [Docs (Old)](https://docs.cyberpanel.net)
|
||||
- 🎓 [Docs (New)](https://community.cyberpanel.net/docs)
|
||||
- ✅ [Changelog](https://community.cyberpanel.net/t/change-logs/161)
|
||||
- 💬 [Forums](https://community.cyberpanel.net)
|
||||
- 📢 [Discord](https://discord.gg/g8k8Db3)
|
||||
- 📵 [Facebook Group](https://www.facebook.com/groups/cyberpanel)
|
||||
- 🎥 [YouTube Channel](https://www.youtube.com/@Cyber-Panel)
|
||||
|
||||
---
|
||||
166
WebTerminal/CPWebSocket.py
Normal file
166
WebTerminal/CPWebSocket.py
Normal file
@@ -0,0 +1,166 @@
|
||||
import tornado.httpserver
|
||||
import tornado.websocket
|
||||
import tornado.ioloop
|
||||
import tornado.web
|
||||
import sys
|
||||
import os
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
|
||||
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
|
||||
import paramiko
|
||||
import os
|
||||
import json
|
||||
import threading as multi
|
||||
import time
|
||||
import asyncio
|
||||
from plogical.processUtilities import ProcessUtilities
|
||||
|
||||
class SSHServer(multi.Thread):
|
||||
OKGREEN = '\033[92m'
|
||||
ENDC = '\033[0m'
|
||||
|
||||
DEFAULT_PORT = 22
|
||||
|
||||
@staticmethod
|
||||
def findSSHPort():
|
||||
try:
|
||||
|
||||
sshData = ProcessUtilities.outputExecutioner('cat /etc/ssh/sshd_config').split('\n')
|
||||
|
||||
for items in sshData:
|
||||
if items.find('Port') > -1 and items[0] != '#':
|
||||
if items[0] == 0:
|
||||
pass
|
||||
else:
|
||||
SSHServer.DEFAULT_PORT = int(items.split(' ')[1])
|
||||
|
||||
logging.writeToFile('SSH Port for WebTerminal Connection: %s' % (SSHServer.DEFAULT_PORT))
|
||||
except BaseException as msg:
|
||||
logging.writeToFile('%s. [SSHServer.findSSHPort]' % (str(msg)))
|
||||
|
||||
def loadPublicKey(self):
|
||||
pubkey = '/root/.ssh/cyberpanel.pub'
|
||||
data = open(pubkey, 'r').read()
|
||||
authFile = '/root/.ssh/authorized_keys'
|
||||
|
||||
checker = 1
|
||||
|
||||
try:
|
||||
authData = open(authFile, 'r').read()
|
||||
if authData.find(data) > -1:
|
||||
checker = 0
|
||||
except:
|
||||
pass
|
||||
|
||||
if checker:
|
||||
writeToFile = open(authFile, 'a')
|
||||
writeToFile.writelines(data)
|
||||
writeToFile.close()
|
||||
|
||||
def __init__(self, websocket):
|
||||
multi.Thread.__init__(self)
|
||||
self.sshclient = paramiko.SSHClient()
|
||||
self.sshclient.load_system_host_keys()
|
||||
self.sshclient.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
k = paramiko.RSAKey.from_private_key_file('/root/.ssh/cyberpanel')
|
||||
|
||||
## Load Public Key
|
||||
self.loadPublicKey()
|
||||
|
||||
self.sshclient.connect('127.0.0.1', SSHServer.DEFAULT_PORT, username='root', pkey=k)
|
||||
self.shell = self.sshclient.invoke_shell(term='xterm')
|
||||
self.shell.settimeout(0)
|
||||
|
||||
self.websocket = websocket
|
||||
self.color = 0
|
||||
|
||||
def recvData(self):
|
||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||
while True:
|
||||
try:
|
||||
if self.websocket.running:
|
||||
if os.path.exists(self.verifyPath) and self.filePassword == self.password:
|
||||
if self.shell.recv_ready():
|
||||
self.websocket.write_message(self.shell.recv(9000).decode("utf-8"))
|
||||
else:
|
||||
time.sleep(0.001)
|
||||
else:
|
||||
return 0
|
||||
else:
|
||||
return 0
|
||||
except BaseException as msg:
|
||||
print('%s. [recvData]' % str(msg))
|
||||
time.sleep(0.001)
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.recvData()
|
||||
except BaseException as msg:
|
||||
print('%s. [SSHServer.run]' % (str(msg)))
|
||||
|
||||
class WSHandler(tornado.websocket.WebSocketHandler):
|
||||
|
||||
def open(self):
|
||||
print('connected')
|
||||
self.running = 1
|
||||
self.sh = SSHServer(self)
|
||||
self.shell = self.sh.shell
|
||||
self.sh.start()
|
||||
self.init = 1
|
||||
print('connect ok')
|
||||
|
||||
def on_message(self, message):
|
||||
try:
|
||||
print('handle message')
|
||||
data = json.loads(message)
|
||||
|
||||
if self.init:
|
||||
self.sh.verifyPath = str(data['data']['verifyPath'])
|
||||
self.sh.password = str(data['data']['password'])
|
||||
self.sh.filePassword = open(self.sh.verifyPath, 'r').read()
|
||||
self.init = 0
|
||||
else:
|
||||
if os.path.exists(self.sh.verifyPath):
|
||||
if self.sh.filePassword == self.sh.password:
|
||||
self.shell.send(str(data['data']))
|
||||
|
||||
except BaseException as msg:
|
||||
print('%s. [WebTerminalServer.handleMessage]' % (str(msg)))
|
||||
|
||||
def on_close(self):
|
||||
print('connection closed')
|
||||
|
||||
def check_origin(self, origin):
|
||||
return True
|
||||
|
||||
application = tornado.web.Application([
|
||||
(r'/', WSHandler),
|
||||
])
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
pidfile = '/usr/local/CyberCP/WebTerminal/pid'
|
||||
|
||||
writeToFile = open(pidfile, 'w')
|
||||
writeToFile.write(str(os.getpid()))
|
||||
writeToFile.close()
|
||||
|
||||
# SSHServer.findSSHPort()
|
||||
#
|
||||
# http_server = tornado.httpserver.HTTPServer(application, ssl_options={
|
||||
# "certfile": "/usr/local/lscp/conf/cert.pem",
|
||||
# "keyfile": "/usr/local/lscp/conf/key.pem",
|
||||
# }, )
|
||||
#
|
||||
# ADDR = '0.0.0.0'
|
||||
# http_server.listen(5678, ADDR)
|
||||
# print('*** Websocket Server Started at %s***' % ADDR)
|
||||
#
|
||||
# import signal
|
||||
# def close_sig_handler(signal, frame):
|
||||
# http_server.stop()
|
||||
# sys.exit()
|
||||
#
|
||||
# signal.signal(signal.SIGINT, close_sig_handler)
|
||||
#
|
||||
# tornado.ioloop.IOLoop.instance().start()
|
||||
0
WebTerminal/__init__.py
Normal file
0
WebTerminal/__init__.py
Normal file
6
WebTerminal/admin.py
Normal file
6
WebTerminal/admin.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
8
WebTerminal/apps.py
Normal file
8
WebTerminal/apps.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class WebterminalConfig(AppConfig):
|
||||
name = 'WebTerminal'
|
||||
12
WebTerminal/cpssh.service
Normal file
12
WebTerminal/cpssh.service
Normal file
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description = CyberPanel SSH Websocket Daemon
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
ExecStart = /usr/local/CyberCP/bin/python /usr/local/CyberCP/WebTerminal/servCTRL.py start
|
||||
ExecStop = /usr/local/CyberCP/bin/python /usr/local/CyberCP/WebTerminal/servCTRL.py stop
|
||||
Restart = /usr/local/CyberCP/bin/python /usr/local/CyberCP/WebTerminal/servCTRL.py restart
|
||||
Restart=on-abnormal
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
0
WebTerminal/migrations/__init__.py
Normal file
0
WebTerminal/migrations/__init__.py
Normal file
6
WebTerminal/models.py
Normal file
6
WebTerminal/models.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
8
WebTerminal/requirments.txt
Normal file
8
WebTerminal/requirments.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
bcrypt==3.1.7
|
||||
cffi==1.13.1
|
||||
cryptography==3.2.1
|
||||
paramiko==2.6.0
|
||||
pycparser==2.19
|
||||
PyNaCl==1.3.0
|
||||
six==1.12.0
|
||||
websockets==9.1
|
||||
51
WebTerminal/servCTRL.py
Normal file
51
WebTerminal/servCTRL.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import subprocess
|
||||
import shlex
|
||||
import argparse
|
||||
import os
|
||||
|
||||
|
||||
|
||||
class servCTRL:
|
||||
pidfile = '/usr/local/CyberCP/WebTerminal/pid'
|
||||
|
||||
def prepareArguments(self):
|
||||
|
||||
parser = argparse.ArgumentParser(description='CyberPanel Policy Control Parser!')
|
||||
parser.add_argument('function', help='Specific a operation to perform!')
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
def start(self):
|
||||
|
||||
if os.path.exists(servCTRL.pidfile):
|
||||
self.stop()
|
||||
|
||||
command = '/usr/local/CyberCP/bin/python /usr/local/CyberCP/WebTerminal/CPWebSocket.py'
|
||||
subprocess.Popen(shlex.split(command))
|
||||
|
||||
def stop(self):
|
||||
try:
|
||||
path = servCTRL.pidfile
|
||||
command = 'kill -9 %s' % (open(path, 'r').read())
|
||||
subprocess.Popen(shlex.split(command))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
policy = servCTRL()
|
||||
args = policy.prepareArguments()
|
||||
|
||||
## Website functions
|
||||
|
||||
if args.function == "start":
|
||||
policy.start()
|
||||
elif args.function == "stop":
|
||||
policy.stop()
|
||||
elif args.function == "restart":
|
||||
policy.stop()
|
||||
policy.start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
132
WebTerminal/static/WebTerminal/main.js
Normal file
132
WebTerminal/static/WebTerminal/main.js
Normal file
@@ -0,0 +1,132 @@
|
||||
var charWidth = 6.2;
|
||||
var charHeight = 15.2;
|
||||
|
||||
/**
|
||||
* for full screen
|
||||
* @returns {{w: number, h: number}}
|
||||
*/
|
||||
function getTerminalSize() {
|
||||
var width = window.innerWidth;
|
||||
var height = window.innerHeight;
|
||||
return {
|
||||
w: Math.floor(width / charWidth),
|
||||
h: Math.floor(height / charHeight)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
function openTerminal(options) {
|
||||
if (!$.isEmptyObject($('.terminal')[0])) {
|
||||
alert("Please refresh this page.");
|
||||
return
|
||||
}
|
||||
|
||||
var client = new WSSHClient();
|
||||
var term = new Terminal({cols: 120, rows: 30, screenKeys: true, useStyle: true});
|
||||
term.on('data', function (data) {
|
||||
client.sendClientData(data);
|
||||
});
|
||||
term.open();
|
||||
$('.terminal').detach().appendTo('#term');
|
||||
$("#term").show();
|
||||
term.write('Connecting...' + '\r\n');
|
||||
|
||||
client.connect({
|
||||
onError: function (error) {
|
||||
term.write('Error connecting to backend.\r\n');
|
||||
//term.destroy();
|
||||
},
|
||||
onConnect: function () {
|
||||
client.sendInitData(options);
|
||||
term.write('connection established..\r\n');
|
||||
},
|
||||
onClose: function (e) {
|
||||
term.write("\r\nconnection closed.")
|
||||
//term.destroy();
|
||||
},
|
||||
onData: function (data) {
|
||||
term.write(data);
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function store(options) {
|
||||
window.localStorage.host = options.host;
|
||||
window.localStorage.port = options.port;
|
||||
window.localStorage.username = options.username;
|
||||
window.localStorage.ispwd = options.ispwd;
|
||||
window.localStorage.secret = options.secret
|
||||
}
|
||||
|
||||
function check() {
|
||||
return validResult["host"] && validResult["port"] && validResult["username"];
|
||||
}
|
||||
|
||||
function connect() {
|
||||
var remember = $("#remember").is(":checked");
|
||||
var options = {
|
||||
verifyPath: $("#verifyPath").text(),
|
||||
password: $("#password").text()
|
||||
};
|
||||
if (remember) {
|
||||
store(options)
|
||||
}
|
||||
openTerminal(options)
|
||||
}
|
||||
|
||||
app.controller('webTerminal', function ($scope, $http, $window) {
|
||||
|
||||
$scope.cyberpanelLoading = true;
|
||||
|
||||
connect();
|
||||
$scope.restartSSH = function (name) {
|
||||
$scope.cyberpanelLoading = false;
|
||||
|
||||
url = "/Terminal/restart";
|
||||
|
||||
var data = {
|
||||
name: name
|
||||
};
|
||||
var config = {
|
||||
headers: {
|
||||
'X-CSRFToken': getCookie('csrftoken')
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
|
||||
|
||||
|
||||
function ListInitialDatas(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
if (response.data.status === 1) {
|
||||
new PNotify({
|
||||
title: 'Success',
|
||||
text: 'Successfully restarted SSH server, refreshing the page now..',
|
||||
type: 'success'
|
||||
});
|
||||
$window.location.href = '/Terminal/';
|
||||
} else {
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: response.data.error_message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function cantLoadInitialDatas(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: 'Could not connect to server, please refresh this page',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
5977
WebTerminal/static/WebTerminal/term.js
Normal file
5977
WebTerminal/static/WebTerminal/term.js
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user