Merge branch 'usmannasir:v2.5.5-dev' into v2.5.5-dev

This commit is contained in:
Master3395
2025-09-20 01:34:53 +02:00
committed by GitHub
7 changed files with 493 additions and 92 deletions

View File

@@ -1,32 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2024-01-01 00:00
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('baseTemplate', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='UserNotificationPreferences',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('backup_notification_dismissed', models.BooleanField(default=False, help_text='Whether user has dismissed the backup notification')),
('ai_scanner_notification_dismissed', models.BooleanField(default=False, help_text='Whether user has dismissed the AI scanner notification')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='notification_preferences', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'User Notification Preferences',
'verbose_name_plural': 'User Notification Preferences',
},
),
]

View File

@@ -124,7 +124,7 @@ class ContainerManager(multi.Thread):
portDef = item.split('/')
portConfig[portDef[0]] = portDef[1]
if image is None or image is '' or tag is None or tag is '':
if image is None or image == '' or tag is None or tag == '':
return redirect(reverse('containerImage'))
Data = {"ownerList": adminNames, "image": image, "name": name, "tag": tag, "portConfig": portConfig,

View File

@@ -1,23 +0,0 @@
# Generated migration for FTP custom quota fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ftp', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='users',
name='custom_quota_enabled',
field=models.BooleanField(default=False, help_text='Enable custom quota for this FTP user'),
),
migrations.AddField(
model_name='users',
name='custom_quota_size',
field=models.IntegerField(default=0, help_text='Custom quota size in MB (0 = use package default)'),
),
]

View File

@@ -657,40 +657,7 @@ password="%s"
# self.setupVirtualEnv(self.distro)
### Applying migrations
os.chdir("/usr/local/CyberCP")
# Try makemigrations first
command = "/usr/local/CyberPanel/bin/python manage.py makemigrations"
result = preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# If makemigrations fails due to migration dependency issues, try to fix it
if result != 1:
preFlightsChecks.stdOut("Migration dependency issue detected, attempting to fix...")
# Reset baseTemplate migrations to resolve dependency issues
command = "/usr/local/CyberPanel/bin/python manage.py migrate baseTemplate zero --fake"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Create a new initial migration for baseTemplate
command = "/usr/local/CyberPanel/bin/python manage.py makemigrations baseTemplate --empty"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# Try makemigrations again
command = "/usr/local/CyberPanel/bin/python manage.py makemigrations"
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
##
# Apply migrations with fake-initial to handle missing initial migrations
command = "/usr/local/CyberPanel/bin/python manage.py migrate --fake-initial"
result = preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
# If fake-initial fails, try regular migrate
if result != 1:
command = "/usr/local/CyberPanel/bin/python manage.py migrate"
preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR)
### Database migrations will be handled after database creation
if not os.path.exists("/usr/local/CyberCP/public"):
os.mkdir("/usr/local/CyberCP/public")
@@ -2917,6 +2884,25 @@ def main():
installCyberPanel.Main(cwd, mysql, distro, ent, serial, port, args.ftp, args.powerdns, args.publicip,
remotemysql, mysqlhost, mysqldb, mysqluser, mysqlpassword, mysqlport)
# Now that database is created, run Django migrations
preFlightsChecks.stdOut("Running Django migrations...")
# Check which directory exists (installation may be at different stages)
if os.path.exists("/usr/local/CyberCP"):
os.chdir("/usr/local/CyberCP")
elif os.path.exists("/usr/local/cyberpanel"):
os.chdir("/usr/local/cyberpanel")
else:
preFlightsChecks.stdOut("ERROR: Neither /usr/local/CyberCP nor /usr/local/cyberpanel exists!")
sys.exit(1)
# Create fresh migrations for all apps
command = "/usr/local/CyberPanel/bin/python manage.py makemigrations"
preFlightsChecks.call(command, distro, command, command, 1, 1, os.EX_OSERR)
# Apply all migrations
command = "/usr/local/CyberPanel/bin/python manage.py migrate"
preFlightsChecks.call(command, distro, command, command, 1, 1, os.EX_OSERR)
checks.setupPHPAndComposer()
checks.fix_selinux_issue()
checks.install_psmisc()

View File

@@ -132,4 +132,446 @@ def fixACMEContexts(request):
logging.writeToFile(str(msg) + " [fixACMEContexts]")
data_ret = {'reconcileStatus': 0, 'error_message': str(msg)}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
# -*- coding: utf-8 -*-
from plogical.httpProc import httpProc
from websiteFunctions.models import Websites, ChildDomains
from loginSystem.models import Administrator
from plogical.virtualHostUtilities import virtualHostUtilities
from django.http import HttpResponse
import json
from plogical.acl import ACLManager
from plogical.processUtilities import ProcessUtilities
# Create your views here.
def loadSSLHome(request):
userID = request.session['userID']
currentACL = ACLManager.loadedACL(userID)
proc = httpProc(request, 'manageSSL/index.html',
currentACL, 'admin')
return proc.render()
def manageSSL(request):
userID = request.session['userID']
currentACL = ACLManager.loadedACL(userID)
websitesName = ACLManager.findAllSites(currentACL, userID)
proc = httpProc(request, 'manageSSL/manageSSL.html',
{'websiteList': websitesName}, 'manageSSL')
return proc.render()
def v2ManageSSL(request):
userID = request.session['userID']
currentACL = ACLManager.loadedACL(userID)
websitesName = ACLManager.findAllSites(currentACL, userID)
data = {}
if ACLManager.CheckForPremFeature('all'):
data['PremStat'] = 1
else:
data['PremStat'] = 0
if request.method == 'POST':
SAVED_CF_Key = request.POST.get('SAVED_CF_Key')
SAVED_CF_Email = request.POST.get('SAVED_CF_Email')
from plogical.dnsUtilities import DNS
DNS.ConfigureCloudflareInAcme(SAVED_CF_Key, SAVED_CF_Email)
data['SaveSuccess'] = 1
RetStatus, SAVED_CF_Key, SAVED_CF_Email = ACLManager.FetchCloudFlareAPIKeyFromAcme()
from plogical.dnsUtilities import DNS
DNS.ConfigurePowerDNSInAcme()
data['SAVED_CF_Key'] = SAVED_CF_Key
data['SAVED_CF_Email'] = SAVED_CF_Email
data['websiteList'] = websitesName
proc = httpProc(request, 'manageSSL/v2ManageSSL.html',
data, 'manageSSL')
return proc.render()
def v2IssueSSL(request):
try:
userID = request.session['userID']
admin = Administrator.objects.get(pk=userID)
try:
if ACLManager.CheckForPremFeature('all'):
if request.method == 'POST':
currentACL = ACLManager.loadedACL(userID)
if currentACL['admin'] == 1:
pass
elif currentACL['manageSSL'] == 1:
pass
else:
return ACLManager.loadErrorJson('SSL', 0)
data = json.loads(request.body)
virtualHost = data['virtualHost']
if ACLManager.checkOwnership(virtualHost, admin, currentACL) == 1:
pass
else:
return ACLManager.loadErrorJson()
try:
website = ChildDomains.objects.get(domain=virtualHost)
adminEmail = website.master.adminEmail
path = website.path
except:
website = Websites.objects.get(domain=virtualHost)
adminEmail = website.adminEmail
path = "/home/" + virtualHost + "/public_html"
## ssl issue
execPath = "/usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/virtualHostUtilities.py"
execPath = execPath + " issueSSLv2 --virtualHostName " + virtualHost + " --administratorEmail " + adminEmail + " --path " + path
output = ProcessUtilities.outputExecutioner(execPath)
if output.find("1,") > -1:
## ssl issue ends
website.ssl = 1
website.save()
# Extract detailed logs from output
logs = output.split("1,", 1)[1] if "1," in output else output
data_ret = {'status': 1, "SSL": 1,
'error_message': "None", 'sslLogs': logs, 'fullOutput': output}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
else:
# Parse error details from output
error_message = output
detailed_error = "SSL issuance failed"
# Check for common ACME errors
if "Rate limit" in output or "rate limit" in output:
detailed_error = "Let's Encrypt rate limit exceeded. Please wait before retrying."
elif "DNS problem" in output or "NXDOMAIN" in output:
detailed_error = "DNS validation failed. Please ensure your domain points to this server."
elif "Connection refused" in output or "Connection timeout" in output:
detailed_error = "Could not connect to ACME server. Check your firewall settings."
elif "Unauthorized" in output or "authorization" in output:
detailed_error = "Domain authorization failed. Verify domain ownership and DNS settings."
elif "CAA record" in output:
detailed_error = "CAA record prevents issuance. Check your DNS CAA records."
elif "Challenge failed" in output or "challenge failed" in output:
detailed_error = "ACME challenge failed. Ensure port 80 is accessible and .well-known path is not blocked."
elif "Invalid response" in output:
detailed_error = "Invalid response from ACME challenge. Check your web server configuration."
else:
# Try to extract the actual error message
if "0," in output:
error_parts = output.split("0,", 1)
if len(error_parts) > 1:
detailed_error = error_parts[1].strip()
data_ret = {'status': 0, "SSL": 0,
'error_message': detailed_error,
'sslLogs': output,
'fullOutput': output,
'technicalDetails': error_message}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
except BaseException as msg:
data_ret = {'status': 0, "SSL": 0,
'error_message': str(msg)}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
except KeyError:
data_ret = {'status': 0, "SSL": 0,
'error_message': str(msg)}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
def issueSSL(request):
try:
userID = request.session['userID']
admin = Administrator.objects.get(pk=userID)
try:
if request.method == 'POST':
currentACL = ACLManager.loadedACL(userID)
if currentACL['admin'] == 1:
pass
elif currentACL['manageSSL'] == 1:
pass
else:
return ACLManager.loadErrorJson('SSL', 0)
data = json.loads(request.body)
virtualHost = data['virtualHost']
if ACLManager.checkOwnership(virtualHost, admin, currentACL) == 1:
pass
else:
return ACLManager.loadErrorJson()
try:
website = ChildDomains.objects.get(domain=virtualHost)
adminEmail = website.master.adminEmail
path = website.path
except:
website = Websites.objects.get(domain=virtualHost)
adminEmail = website.adminEmail
path = "/home/" + virtualHost + "/public_html"
## ssl issue
execPath = "/usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/virtualHostUtilities.py"
execPath = execPath + " issueSSL --virtualHostName " + virtualHost + " --administratorEmail " + adminEmail + " --path " + path
output = ProcessUtilities.outputExecutioner(execPath)
if output.find("1,None") > -1:
pass
else:
data_ret = {'status': 0, "SSL": 0,
'error_message': output}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
## ssl issue ends
website.ssl = 1
website.save()
data_ret = {'status': 1, "SSL": 1,
'error_message': "None"}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
except BaseException as msg:
data_ret = {'status': 0, "SSL": 0,
'error_message': str(msg)}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
except KeyError:
data_ret = {'status': 0, "SSL": 0,
'error_message': str(msg)}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
def sslForHostName(request):
userID = request.session['userID']
currentACL = ACLManager.loadedACL(userID)
websitesName = ACLManager.findAllSites(currentACL, userID, 1)
proc = httpProc(request, 'manageSSL/sslForHostName.html',
{'websiteList': websitesName}, 'hostnameSSL')
return proc.render()
def obtainHostNameSSL(request):
try:
userID = request.session['userID']
try:
if request.method == 'POST':
currentACL = ACLManager.loadedACL(userID)
if currentACL['admin'] == 1:
pass
elif currentACL['hostnameSSL'] == 1:
pass
else:
return ACLManager.loadErrorJson('SSL', 0)
data = json.loads(request.body)
virtualHost = data['virtualHost']
try:
website = Websites.objects.get(domain=virtualHost)
path = "/home/" + virtualHost + "/public_html"
except:
website = ChildDomains.objects.get(domain=virtualHost)
path = website.path
admin = Administrator.objects.get(pk=userID)
if ACLManager.checkOwnership(virtualHost, admin, currentACL) == 1:
pass
else:
return ACLManager.loadErrorJson()
## ssl issue
execPath = "/usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/virtualHostUtilities.py"
execPath = execPath + " issueSSLForHostName --virtualHostName " + virtualHost + " --path " + path
output = ProcessUtilities.outputExecutioner(execPath)
if output.find("1,None") > -1:
data_ret = {"status": 1, "SSL": 1,
'error_message': "None"}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
else:
data_ret = {"status": 0, "SSL": 0,
'error_message': output}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
## ssl issue ends
except BaseException as msg:
data_ret = {"status": 0, "SSL": 0,
'error_message': str(msg)}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
except KeyError:
data_ret = {"status": 0, "SSL": 0,
'error_message': str(msg)}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
def sslForMailServer(request):
userID = request.session['userID']
currentACL = ACLManager.loadedACL(userID)
websitesName = ACLManager.findAllSites(currentACL, userID)
websitesName = websitesName + ACLManager.findChildDomains(websitesName)
proc = httpProc(request, 'manageSSL/sslForMailServer.html',
{'websiteList': websitesName}, 'mailServerSSL')
return proc.render()
def obtainMailServerSSL(request):
try:
userID = request.session['userID']
try:
if request.method == 'POST':
currentACL = ACLManager.loadedACL(userID)
if currentACL['admin'] == 1:
pass
elif currentACL['mailServerSSL'] == 1:
pass
else:
return ACLManager.loadErrorJson('SSL', 0)
data = json.loads(request.body)
virtualHost = data['virtualHost']
admin = Administrator.objects.get(pk=userID)
if ACLManager.checkOwnership(virtualHost, admin, currentACL) == 1:
pass
else:
return ACLManager.loadErrorJson()
path = "/home/" + virtualHost + "/public_html"
## ssl issue
execPath = "/usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/virtualHostUtilities.py"
execPath = execPath + " issueSSLForMailServer --virtualHostName " + virtualHost + " --path " + path
output = ProcessUtilities.outputExecutioner(execPath)
if output.find("1,None") > -1:
data_ret = {"status": 1, "SSL": 1,
'error_message': "None"}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
else:
data_ret = {"status": 0, "SSL": 0,
'error_message': output}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
## ssl issue ends
except BaseException as msg:
data_ret = {"status": 0, "SSL": 0,
'error_message': str(msg)}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
except KeyError as msg:
data_ret = {"status": 0, "SSL": 0,
'error_message': str(msg)}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
def getSSLDetails(request):
try:
userID = request.session['userID']
admin = Administrator.objects.get(pk=userID)
try:
if request.method == 'POST':
currentACL = ACLManager.loadedACL(userID)
if currentACL['admin'] == 1:
pass
elif currentACL['manageSSL'] == 1:
pass
else:
return ACLManager.loadErrorJson('SSL', 0)
data = json.loads(request.body)
virtualHost = data['virtualHost']
if ACLManager.checkOwnership(virtualHost, admin, currentACL) == 1:
pass
else:
return ACLManager.loadErrorJson()
try:
website = ChildDomains.objects.get(domain=virtualHost)
except:
website = Websites.objects.get(domain=virtualHost)
try:
import OpenSSL
from datetime import datetime
filePath = '/etc/letsencrypt/live/%s/fullchain.pem' % (virtualHost)
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
open(filePath, 'r').read())
expireData = x509.get_notAfter().decode('ascii')
finalDate = datetime.strptime(expireData, '%Y%m%d%H%M%SZ')
now = datetime.now()
diff = finalDate - now
data_ret = {
'status': 1,
'hasSSL': True,
'days': str(diff.days),
'authority': x509.get_issuer().get_components()[1][1].decode('utf-8'),
'expiryDate': finalDate.strftime('%Y-%m-%d %H:%M:%S')
}
if data_ret['authority'] == 'Denial':
data_ret['authority'] = 'SELF-SIGNED SSL'
except BaseException as msg:
data_ret = {
'status': 1,
'hasSSL': False,
'error_message': str(msg)
}
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)
except KeyError:
data_ret = {'status': 0, 'error_message': 'Not logged in'}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)

View File

@@ -2204,5 +2204,33 @@ def fetchWPBackups(request):
return coreResult
except KeyError:
return redirect(loadLoginPage)
def fixSubdomainLogs(request):
try:
userID = request.session['userID']
wm = WebsiteManager()
coreResult = wm.fixSubdomainLogs(userID)
return coreResult
except KeyError:
return redirect(loadLoginPage)
def fixSubdomainLogsAction(request):
try:
userID = request.session['userID']
wm = WebsiteManager()
coreResult = wm.fixSubdomainLogsAction(userID)
return coreResult
except KeyError:
return redirect(loadLoginPage)

View File

@@ -3978,7 +3978,7 @@ context /cyberpanel_suspension_page.html {
if output.find("1,None") > -1:
status = {"configstatus": 1, "message": "vHost configuration reset to default successfully."}
else:
status = {"configstatus": 0, "error_message": f"Failed to reset configuration: {output}"
status = {"configstatus": 0, "error_message": f"Failed to reset configuration: {output}"}
final_json = json.dumps(status)
return HttpResponse(final_json)
@@ -7586,7 +7586,7 @@ StrictHostKeyChecking no
if output.find("1,None") > -1:
status = {"status": 1, "message": "Apache configuration reset to default successfully."}
else:
status = {"status": 0, "error_message": f"Failed to reset Apache configuration: {output}"
status = {"status": 0, "error_message": f"Failed to reset Apache configuration: {output}"}
final_json = json.dumps(status)
return HttpResponse(final_json)