diff --git a/baseTemplate/migrations/0002_usernotificationpreferences.py b/baseTemplate/migrations/0002_usernotificationpreferences.py deleted file mode 100644 index 3db48ac84..000000000 --- a/baseTemplate/migrations/0002_usernotificationpreferences.py +++ /dev/null @@ -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', - }, - ), - ] diff --git a/dockerManager/container.py b/dockerManager/container.py index 8458da9f4..6d98882c8 100644 --- a/dockerManager/container.py +++ b/dockerManager/container.py @@ -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, diff --git a/ftp/migrations/0002_add_custom_quota_fields.py b/ftp/migrations/0002_add_custom_quota_fields.py deleted file mode 100644 index 6058116d6..000000000 --- a/ftp/migrations/0002_add_custom_quota_fields.py +++ /dev/null @@ -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)'), - ), - ] diff --git a/install/install.py b/install/install.py index 50b165258..b16c9504a 100644 --- a/install/install.py +++ b/install/install.py @@ -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() diff --git a/manageSSL/views.py b/manageSSL/views.py index bdd494bb6..e67db2610 100644 --- a/manageSSL/views.py +++ b/manageSSL/views.py @@ -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) \ No newline at end of file diff --git a/websiteFunctions/views.py b/websiteFunctions/views.py index 1dcc7fedf..7b7a4d622 100644 --- a/websiteFunctions/views.py +++ b/websiteFunctions/views.py @@ -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) \ No newline at end of file diff --git a/websiteFunctions/website.py b/websiteFunctions/website.py index 083707355..3230e804e 100644 --- a/websiteFunctions/website.py +++ b/websiteFunctions/website.py @@ -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)