diff --git a/baseTemplate/templates/baseTemplate/index.html b/baseTemplate/templates/baseTemplate/index.html index 63928508b..882cea420 100644 --- a/baseTemplate/templates/baseTemplate/index.html +++ b/baseTemplate/templates/baseTemplate/index.html @@ -1180,6 +1180,9 @@ List Sub/Addon Domains + + Fix Subdomain Logs + {% if admin or modifyWebsite %} Modify Website diff --git a/plogical/management/commands/fix_subdomain_logs.py b/plogical/management/commands/fix_subdomain_logs.py new file mode 100644 index 000000000..bfbe287b9 --- /dev/null +++ b/plogical/management/commands/fix_subdomain_logs.py @@ -0,0 +1,190 @@ +#!/usr/local/CyberCP/bin/python +""" +Django management command to fix subdomain log configurations +Usage: python manage.py fix_subdomain_logs [--dry-run] [--domain ] +""" + +import os +import sys +import django +import re +import shutil +from datetime import datetime + +# Add CyberPanel to Python path +sys.path.append('/usr/local/CyberCP') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") +django.setup() + +from django.core.management.base import BaseCommand, CommandError +from websiteFunctions.models import ChildDomains, Websites +from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging +from plogical.processUtilities import ProcessUtilities + + +class Command(BaseCommand): + help = 'Fix subdomain log configurations to use separate log files' + + def add_arguments(self, parser): + parser.add_argument( + '--dry-run', + action='store_true', + help='Show what would be changed without making changes', + ) + parser.add_argument( + '--domain', + type=str, + help='Fix logs for a specific domain only', + ) + parser.add_argument( + '--backup', + action='store_true', + help='Create backup of original configurations', + ) + + def handle(self, *args, **options): + dry_run = options['dry_run'] + specific_domain = options['domain'] + create_backup = options['backup'] + + if dry_run: + self.stdout.write( + self.style.WARNING('DRY RUN MODE - No changes will be made') + ) + + self.stdout.write('Starting subdomain log configuration fix...') + + try: + if specific_domain: + # Fix specific domain + self.fix_domain_logs(specific_domain, dry_run, create_backup) + else: + # Fix all child domains + child_domains = ChildDomains.objects.all() + fixed_count = 0 + + for child_domain in child_domains: + if self.fix_domain_logs(child_domain.domain, dry_run, create_backup): + fixed_count += 1 + + self.stdout.write( + self.style.SUCCESS(f'Fixed log configurations for {fixed_count} child domains') + ) + + except Exception as e: + raise CommandError(f'Failed to fix subdomain logs: {str(e)}') + + def fix_domain_logs(self, domain_name, dry_run=False, create_backup=False): + """Fix log configuration for a specific domain""" + try: + # Get child domain info + try: + child_domain = ChildDomains.objects.get(domain=domain_name) + master_domain = child_domain.master.domain + domain_path = child_domain.path + except ChildDomains.DoesNotExist: + self.stdout.write( + self.style.WARNING(f'Domain {domain_name} is not a child domain, skipping') + ) + return False + + vhost_conf_path = f"/usr/local/lsws/conf/vhosts/{domain_name}/vhost.conf" + + if not os.path.exists(vhost_conf_path): + self.stdout.write( + self.style.WARNING(f'VHost config not found for {domain_name}: {vhost_conf_path}') + ) + return False + + # Read current configuration + with open(vhost_conf_path, 'r') as f: + config_content = f.read() + + # Check if fix is needed + if f'{master_domain}.error_log' not in config_content and f'{master_domain}.access_log' not in config_content: + self.stdout.write(f'✓ {domain_name} already has correct log configuration') + return True + + self.stdout.write(f'Fixing log configuration for {domain_name}...') + + if dry_run: + self.stdout.write(f' [DRY RUN] Would fix log paths in {vhost_conf_path}') + return True + + # Create backup if requested + if create_backup: + backup_path = f"{vhost_conf_path}.backup.{datetime.now().strftime('%Y%m%d_%H%M%S')}" + shutil.copy2(vhost_conf_path, backup_path) + self.stdout.write(f' Created backup: {backup_path}') + + # Fix the configuration + fixed_content = config_content + + # Fix error log path + fixed_content = re.sub( + rf'errorlog\s+\$VH_ROOT/logs/{re.escape(master_domain)}\.error_log', + f'errorlog $VH_ROOT/logs/{domain_name}.error_log', + fixed_content + ) + + # Fix access log path + fixed_content = re.sub( + rf'accesslog\s+\$VH_ROOT/logs/{re.escape(master_domain)}\.access_log', + f'accesslog $VH_ROOT/logs/{domain_name}.access_log', + fixed_content + ) + + # Fix CustomLog paths (for Apache configurations) + fixed_content = re.sub( + rf'CustomLog\s+/home/{re.escape(master_domain)}/logs/{re.escape(master_domain)}\.access_log', + f'CustomLog /home/{domain_name}/logs/{domain_name}.access_log', + fixed_content + ) + + # Write the fixed configuration + with open(vhost_conf_path, 'w') as f: + f.write(fixed_content) + + # Set proper ownership + ProcessUtilities.executioner(f'chown lsadm:lsadm {vhost_conf_path}') + + # Create the log directory if it doesn't exist + log_dir = f"/home/{master_domain}/logs" + if not os.path.exists(log_dir): + os.makedirs(log_dir, exist_ok=True) + ProcessUtilities.executioner(f'chown -R {child_domain.master.externalApp}:{child_domain.master.externalApp} {log_dir}') + + # Create separate log files for the child domain + error_log_path = f"{log_dir}/{domain_name}.error_log" + access_log_path = f"{log_dir}/{domain_name}.access_log" + + # Create empty log files if they don't exist + for log_path in [error_log_path, access_log_path]: + if not os.path.exists(log_path): + with open(log_path, 'w') as f: + f.write('') + ProcessUtilities.executioner(f'chown {child_domain.master.externalApp}:{child_domain.master.externalApp} {log_path}') + ProcessUtilities.executioner(f'chmod 644 {log_path}') + + # Restart LiteSpeed to apply changes + ProcessUtilities.executioner('systemctl restart lsws') + + self.stdout.write(f'✓ Fixed log configuration for {domain_name}') + logging.writeToFile(f'Fixed subdomain log configuration for {domain_name}') + + return True + + except Exception as e: + self.stdout.write( + self.style.ERROR(f'Failed to fix logs for {domain_name}: {str(e)}') + ) + logging.writeToFile(f'Error fixing subdomain logs for {domain_name}: {str(e)}') + return False + + def show_help_examples(self): + """Show usage examples""" + self.stdout.write('\nUsage examples:') + self.stdout.write(' python manage.py fix_subdomain_logs --dry-run') + self.stdout.write(' python manage.py fix_subdomain_logs --domain diabetes.example.com') + self.stdout.write(' python manage.py fix_subdomain_logs --backup') + self.stdout.write(' python manage.py fix_subdomain_logs --domain diabetes.example.com --backup --dry-run') diff --git a/plogical/upgrade.py b/plogical/upgrade.py index 6386b2538..3588c5d7a 100644 --- a/plogical/upgrade.py +++ b/plogical/upgrade.py @@ -2611,6 +2611,156 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL except: pass + @staticmethod + def fixSubdomainLogConfigurations(): + """Fix subdomain log configurations during upgrade""" + try: + # Check if this fix has already been applied + fix_marker_file = '/usr/local/lscp/logs/subdomain_log_fix_applied' + if os.path.exists(fix_marker_file): + Upgrade.stdOut("Subdomain log fix already applied - skipping") + return + + Upgrade.stdOut("=== FIXING SUBDOMAIN LOG CONFIGURATIONS ===") + + # Import required modules + import sys + import os + sys.path.append('/usr/local/CyberCP') + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") + + try: + import django + django.setup() + + from websiteFunctions.models import ChildDomains + from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging + from plogical.processUtilities import ProcessUtilities + import re + import shutil + from datetime import datetime + + # Get all child domains + child_domains = ChildDomains.objects.all() + + if not child_domains: + Upgrade.stdOut("No child domains found - skipping subdomain log fix") + return + + Upgrade.stdOut(f"Found {len(child_domains)} child domains to check") + + fixed_count = 0 + skipped_count = 0 + + for child_domain in child_domains: + domain_name = child_domain.domain + master_domain = child_domain.master.domain + + vhost_conf_path = f"/usr/local/lsws/conf/vhosts/{domain_name}/vhost.conf" + + if not os.path.exists(vhost_conf_path): + Upgrade.stdOut(f"⚠️ Skipping {domain_name}: vHost config not found") + skipped_count += 1 + continue + + try: + # Read current configuration + with open(vhost_conf_path, 'r') as f: + config_content = f.read() + + # Check if fix is needed + if f'{master_domain}.error_log' not in config_content and f'{master_domain}.access_log' not in config_content: + Upgrade.stdOut(f"✅ {domain_name}: Already has correct log configuration") + skipped_count += 1 + continue + + # Create backup + backup_path = f"{vhost_conf_path}.backup.{datetime.now().strftime('%Y%m%d_%H%M%S')}" + shutil.copy2(vhost_conf_path, backup_path) + + # Fix the configuration + fixed_content = config_content + + # Fix error log path + fixed_content = re.sub( + rf'errorlog\s+\$VH_ROOT/logs/{re.escape(master_domain)}\.error_log', + f'errorlog $VH_ROOT/logs/{domain_name}.error_log', + fixed_content + ) + + # Fix access log path + fixed_content = re.sub( + rf'accesslog\s+\$VH_ROOT/logs/{re.escape(master_domain)}\.access_log', + f'accesslog $VH_ROOT/logs/{domain_name}.access_log', + fixed_content + ) + + # Fix CustomLog paths (for Apache configurations) + fixed_content = re.sub( + rf'CustomLog\s+/home/{re.escape(master_domain)}/logs/{re.escape(master_domain)}\.access_log', + f'CustomLog /home/{domain_name}/logs/{domain_name}.access_log', + fixed_content + ) + + # Write the fixed configuration + with open(vhost_conf_path, 'w') as f: + f.write(fixed_content) + + # Set proper ownership + ProcessUtilities.executioner(f'chown lsadm:lsadm {vhost_conf_path}') + + # Create the log directory if it doesn't exist + log_dir = f"/home/{master_domain}/logs" + if not os.path.exists(log_dir): + os.makedirs(log_dir, exist_ok=True) + ProcessUtilities.executioner(f'chown -R {child_domain.master.externalApp}:{child_domain.master.externalApp} {log_dir}') + + # Create separate log files for the child domain + error_log_path = f"{log_dir}/{domain_name}.error_log" + access_log_path = f"{log_dir}/{domain_name}.access_log" + + # Create empty log files if they don't exist + for log_path in [error_log_path, access_log_path]: + if not os.path.exists(log_path): + with open(log_path, 'w') as f: + f.write('') + ProcessUtilities.executioner(f'chown {child_domain.master.externalApp}:{child_domain.master.externalApp} {log_path}') + ProcessUtilities.executioner(f'chmod 644 {log_path}') + + Upgrade.stdOut(f"✅ Fixed log configuration for {domain_name}") + logging.writeToFile(f'Fixed subdomain log configuration for {domain_name} during upgrade') + fixed_count += 1 + + except Exception as e: + Upgrade.stdOut(f"❌ Failed to fix {domain_name}: {str(e)}") + logging.writeToFile(f'Error fixing subdomain logs for {domain_name} during upgrade: {str(e)}') + + # Restart LiteSpeed to apply changes if any were made + if fixed_count > 0: + Upgrade.stdOut("Restarting LiteSpeed to apply log configuration changes...") + ProcessUtilities.executioner('systemctl restart lsws') + + Upgrade.stdOut(f"=== SUBDOMAIN LOG FIX COMPLETE ===") + Upgrade.stdOut(f"Fixed: {fixed_count} domains") + Upgrade.stdOut(f"Skipped: {skipped_count} domains") + + # Create marker file to indicate fix has been applied + try: + with open(fix_marker_file, 'w') as f: + f.write(f"Subdomain log fix applied on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") + f.write(f"Fixed domains: {fixed_count}\n") + f.write(f"Skipped domains: {skipped_count}\n") + except: + pass + + except ImportError as e: + Upgrade.stdOut(f"⚠️ Django not available during upgrade: {str(e)}") + Upgrade.stdOut("Subdomain log fix will be applied on next CyberPanel restart") + + except Exception as e: + Upgrade.stdOut(f"❌ Error in subdomain log fix: {str(e)}") + logging.writeToFile(f'Error in subdomain log fix during upgrade: {str(e)}') + @staticmethod def enableServices(): try: @@ -4396,6 +4546,9 @@ pm.max_spare_servers = 3 # Fix LiteSpeed configuration files if missing Upgrade.fixLiteSpeedConfig() + + # Fix subdomain log configurations + Upgrade.fixSubdomainLogConfigurations() ### General migrations are not needed any more diff --git a/plogical/vhostConfs.py b/plogical/vhostConfs.py index 21bc461f6..d9e88471f 100644 --- a/plogical/vhostConfs.py +++ b/plogical/vhostConfs.py @@ -110,13 +110,13 @@ index { indexFiles index.php, index.html } -errorlog $VH_ROOT/logs/{masterDomain}.error_log { +errorlog $VH_ROOT/logs/{virtualHostName}.error_log { useServer 0 logLevel WARN rollingSize 10M } -accesslog $VH_ROOT/logs/{masterDomain}.access_log { +accesslog $VH_ROOT/logs/{virtualHostName}.access_log { useServer 0 logFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" logHeaders 5 @@ -204,7 +204,7 @@ context /.well-known/acme-challenge { SuexecUserGroup {externalApp} {externalApp} DocumentRoot {path} Alias /.well-known/acme-challenge /home/{virtualHostName}/public_html/.well-known/acme-challenge - CustomLog /home/{masterDomain}/logs/{masterDomain}.access_log combined + CustomLog /home/{virtualHostName}/logs/{virtualHostName}.access_log combined AddHandler application/x-httpd-php{php} .php .php7 .phtml CacheRoot lscache @@ -474,7 +474,7 @@ pm.max_spare_servers = {pmMaxSpareServers} "phpVersion": {php}, "custom_conf": { ServerAdmin {administratorEmail} - CustomLog /home/{masterDomain}/logs/{masterDomain}.access_log combined + CustomLog /home/{virtualHostName}/logs/{virtualHostName}.access_log combined CacheRoot /home/{masterDomain}/lscache @@ -489,7 +489,7 @@ pm.max_spare_servers = {pmMaxSpareServers} "phpVersion": {php}, "custom_conf": { ServerAdmin {administratorEmail} - CustomLog /home/{masterDomain}/logs/{masterDomain}.access_log combined + CustomLog /home/{virtualHostName}/logs/{virtualHostName}.access_log combined CacheRoot /home/{masterDomain}/lscache diff --git a/test_subdomain_log_fix.py b/test_subdomain_log_fix.py new file mode 100644 index 000000000..37b67d501 --- /dev/null +++ b/test_subdomain_log_fix.py @@ -0,0 +1,120 @@ +#!/usr/local/CyberCP/bin/python +""" +Test script for subdomain log fix +This script tests the subdomain log fix functionality +""" + +import os +import sys +import django + +# Add CyberPanel to Python path +sys.path.append('/usr/local/CyberCP') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") +django.setup() + +from websiteFunctions.models import ChildDomains +from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging + + +def test_subdomain_log_configuration(): + """Test if subdomain log configurations are correct""" + print("Testing subdomain log configurations...") + + issues_found = 0 + child_domains = ChildDomains.objects.all() + + if not child_domains: + print("No child domains found.") + return True + + for child_domain in child_domains: + domain_name = child_domain.domain + master_domain = child_domain.master.domain + + vhost_conf_path = f"/usr/local/lsws/conf/vhosts/{domain_name}/vhost.conf" + + if not os.path.exists(vhost_conf_path): + print(f"⚠️ VHost config not found for {domain_name}") + issues_found += 1 + continue + + try: + with open(vhost_conf_path, 'r') as f: + config_content = f.read() + + # Check for incorrect log paths + if f'{master_domain}.error_log' in config_content: + print(f"❌ {domain_name}: Using master domain error log") + issues_found += 1 + else: + print(f"✅ {domain_name}: Error log configuration OK") + + if f'{master_domain}.access_log' in config_content: + print(f"❌ {domain_name}: Using master domain access log") + issues_found += 1 + else: + print(f"✅ {domain_name}: Access log configuration OK") + + except Exception as e: + print(f"❌ {domain_name}: Error reading config - {str(e)}") + issues_found += 1 + + if issues_found == 0: + print("\n🎉 All subdomain log configurations are correct!") + return True + else: + print(f"\n⚠️ Found {issues_found} issues with subdomain log configurations") + return False + + +def test_management_command(): + """Test the management command""" + print("\nTesting management command...") + + try: + from django.core.management import call_command + from io import StringIO + + # Test dry run + out = StringIO() + call_command('fix_subdomain_logs', '--dry-run', stdout=out) + print("✅ Management command dry run works") + + return True + except Exception as e: + print(f"❌ Management command test failed: {str(e)}") + return False + + +def main(): + """Main test function""" + print("=" * 60) + print("SUBDOMAIN LOG FIX TEST") + print("=" * 60) + + # Test 1: Check current configurations + config_test = test_subdomain_log_configuration() + + # Test 2: Test management command + cmd_test = test_management_command() + + print("\n" + "=" * 60) + print("TEST SUMMARY") + print("=" * 60) + + if config_test and cmd_test: + print("🎉 All tests passed!") + print("\nTo fix any issues found:") + print("1. Via Web Interface: Websites > Fix Subdomain Logs") + print("2. Via CLI: python manage.py fix_subdomain_logs --all") + print("3. For specific domain: python manage.py fix_subdomain_logs --domain example.com") + return True + else: + print("⚠️ Some tests failed. Check the output above.") + return False + + +if __name__ == "__main__": + success = main() + sys.exit(0 if success else 1) diff --git a/websiteFunctions/static/websiteFunctions/websiteFunctions.js b/websiteFunctions/static/websiteFunctions/websiteFunctions.js index 7a4d76d04..562eeddb8 100644 --- a/websiteFunctions/static/websiteFunctions/websiteFunctions.js +++ b/websiteFunctions/static/websiteFunctions/websiteFunctions.js @@ -13289,6 +13289,16 @@ app.controller('manageAliasController', function ($scope, $http, $timeout, $wind $scope.manageAliasLoading = true; $scope.operationSuccess = true; + // Initialize the page to show aliases list + $scope.showAliasesList = function() { + $scope.aliasTable = true; + $scope.addAliasButton = true; + $scope.domainAliasForm = false; + }; + + // Auto-show aliases list on page load + $scope.showAliasesList(); + $scope.createAliasEnter = function ($event) { var keyCode = $event.which || $event.keyCode; if (keyCode === 13) { @@ -13536,6 +13546,64 @@ app.controller('manageAliasController', function ($scope, $http, $timeout, $wind }; + $scope.issueAliasSSL = function (masterDomain, aliasDomain) { + $scope.manageAliasLoading = false; + + url = "/websites/issueAliasSSL"; + + var data = { + masterDomain: masterDomain, + aliasDomain: aliasDomain + }; + + var config = { + headers: { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas); + + function ListInitialDatas(response) { + if (response.data.issueAliasSSL === 1) { + $scope.aliasTable = false; + $scope.addAliasButton = true; + $scope.domainAliasForm = true; + $scope.aliasError = true; + $scope.couldNotConnect = true; + $scope.aliasCreated = true; + $scope.manageAliasLoading = true; + $scope.operationSuccess = false; + + $timeout(function () { + $window.location.reload(); + }, 3000); + } else { + $scope.aliasTable = false; + $scope.addAliasButton = true; + $scope.domainAliasForm = true; + $scope.aliasError = false; + $scope.couldNotConnect = true; + $scope.aliasCreated = true; + $scope.manageAliasLoading = true; + $scope.operationSuccess = true; + + $scope.errorMessage = response.data.error_message; + } + } + + function cantLoadInitialDatas(response) { + $scope.aliasTable = false; + $scope.addAliasButton = true; + $scope.domainAliasForm = true; + $scope.aliasError = true; + $scope.couldNotConnect = false; + $scope.aliasCreated = true; + $scope.manageAliasLoading = true; + $scope.operationSuccess = true; + } + }; + ////// create domain part diff --git a/websiteFunctions/templates/websiteFunctions/domainAlias.html b/websiteFunctions/templates/websiteFunctions/domainAlias.html index 8e2162449..5ad7e01b7 100644 --- a/websiteFunctions/templates/websiteFunctions/domainAlias.html +++ b/websiteFunctions/templates/websiteFunctions/domainAlias.html @@ -175,37 +175,52 @@
- - - - - - - - - - - - - - - - - - - -
DomainLaunchPathSSLDelete
- - - - -
+ +
+

{% trans "No domain aliases found for this domain." %}

+
+ + +
+ + + + + + + + + + + + {% for alias in aliases %} + + + + + + + + {% endfor %} + +
{% trans "Alias Domain" %}{% trans "Master Domain" %}{% trans "Launch" %}{% trans "SSL" %}{% trans "Delete" %}
{{ alias }}{{ masterDomain }} + + + + + + + +
+
diff --git a/websiteFunctions/templates/websiteFunctions/fixSubdomainLogs.html b/websiteFunctions/templates/websiteFunctions/fixSubdomainLogs.html new file mode 100644 index 000000000..3a12cfc8a --- /dev/null +++ b/websiteFunctions/templates/websiteFunctions/fixSubdomainLogs.html @@ -0,0 +1,334 @@ +{% extends "baseTemplate/index.html" %} +{% load static %} +{% load i18n %} + +{% block title %}Fix Subdomain Logs{% endblock %} + +{% block content %} +
+
+ +
+ +
+
+ +
+
+

Subdomain Log Issue Fix

+
+
+
+
Issue Description
+

This tool fixes a known issue where child domains (subdomains) share log files with their master domains, causing log contamination. This fix ensures each subdomain has its own separate log files.

+
+ +
+
What This Fix Does
+
    +
  • Updates vHost configurations to use separate log files for each subdomain
  • +
  • Creates individual log files: subdomain.error_log and subdomain.access_log
  • +
  • Restarts LiteSpeed to apply changes
  • +
  • Maintains proper file ownership and permissions
  • +
+
+
+
+ + +
+
+

Fix Options

+
+
+
+ {% csrf_token %} + +
+
+
+ + +
+
+ + +
+ +
+
+
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+
+ +
+ + +
+
+
+
+ + + + + +
+
+

Current Child Domains

+
+
+
+ + + + + + + + + + + + +
DomainMaster DomainLog StatusActions
+
+
+
+
+
+
+ + +{% endblock %} diff --git a/websiteFunctions/urls.py b/websiteFunctions/urls.py index 891d8837c..8d56bc1bb 100644 --- a/websiteFunctions/urls.py +++ b/websiteFunctions/urls.py @@ -207,5 +207,9 @@ urlpatterns = [ path('get_website_resources/', views.get_website_resources, name='get_website_resources'), + # Subdomain Log Fix + path('fixSubdomainLogs', views.fixSubdomainLogs, name='fixSubdomainLogs'), + path('fixSubdomainLogsAction', views.fixSubdomainLogsAction, name='fixSubdomainLogsAction'), + ] diff --git a/websiteFunctions/website.py b/websiteFunctions/website.py index 51cbeeff4..083707355 100644 --- a/websiteFunctions/website.py +++ b/websiteFunctions/website.py @@ -3936,6 +3936,7 @@ context /cyberpanel_suspension_page.html { if is_child: # Use child domain template default_config = vhostConfs.olsChildConf + default_config = default_config.replace('{virtualHostName}', virtualHost) default_config = default_config.replace('{path}', path) default_config = default_config.replace('{masterDomain}', master_domain) default_config = default_config.replace('{adminEmails}', admin_email) @@ -8200,3 +8201,174 @@ StrictHostKeyChecking no json_data = json.dumps(data_ret) return HttpResponse(json_data) + def fixSubdomainLogs(self, request): + """Display subdomain log fix interface""" + try: + currentACL = ACLManager.loadedACL(request.user.pk) + admin = ACLManager.loadedAdmin(request.user.pk) + + if ACLManager.currentContextPermission(currentACL, 'websites') == 0: + return ACLManager.loadErrorJson('websites', 0) + + return render(request, 'websiteFunctions/fixSubdomainLogs.html', { + 'acls': currentACL, + 'admin': admin + }) + + except BaseException as msg: + logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [fixSubdomainLogs]") + return ACLManager.loadErrorJson('websites', 0) + + def fixSubdomainLogsAction(self, request): + """Execute subdomain log fix""" + try: + currentACL = ACLManager.loadedACL(request.user.pk) + admin = ACLManager.loadedAdmin(request.user.pk) + + if ACLManager.currentContextPermission(currentACL, 'websites') == 0: + return ACLManager.loadErrorJson('websites', 0) + + action = request.POST.get('action') + domain = request.POST.get('domain', '').strip() + dry_run = request.POST.get('dry_run', 'false').lower() == 'true' + create_backup = request.POST.get('create_backup', 'false').lower() == 'true' + + if action == 'fix_all': + # Fix all child domains + from websiteFunctions.models import ChildDomains + child_domains = ChildDomains.objects.all() + fixed_count = 0 + failed_domains = [] + + for child_domain in child_domains: + if self._fix_single_domain_logs(child_domain.domain, dry_run, create_backup): + fixed_count += 1 + else: + failed_domains.append(child_domain.domain) + + if failed_domains: + message = f"Fixed {fixed_count} domains. Failed: {', '.join(failed_domains)}" + else: + message = f"Successfully fixed {fixed_count} child domains" + + data_ret = {'status': 1, 'message': message} + + elif action == 'fix_domain' and domain: + # Fix specific domain + if self._fix_single_domain_logs(domain, dry_run, create_backup): + data_ret = {'status': 1, 'message': f"Successfully fixed logs for {domain}"} + else: + data_ret = {'status': 0, 'error_message': f"Failed to fix logs for {domain}"} + else: + data_ret = {'status': 0, 'error_message': "Invalid action or missing domain"} + + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + except BaseException as msg: + logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [fixSubdomainLogsAction]") + data_ret = {'status': 0, 'error_message': str(msg)} + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + def _fix_single_domain_logs(self, domain_name, dry_run=False, create_backup=False): + """Fix log configuration for a single domain""" + try: + import re + import shutil + from datetime import datetime + from websiteFunctions.models import ChildDomains + + # Get child domain info + try: + child_domain = ChildDomains.objects.get(domain=domain_name) + master_domain = child_domain.master.domain + domain_path = child_domain.path + except ChildDomains.DoesNotExist: + logging.CyberCPLogFileWriter.writeToFile(f'Domain {domain_name} is not a child domain') + return False + + vhost_conf_path = f"/usr/local/lsws/conf/vhosts/{domain_name}/vhost.conf" + + if not os.path.exists(vhost_conf_path): + logging.CyberCPLogFileWriter.writeToFile(f'VHost config not found for {domain_name}') + return False + + # Read current configuration + with open(vhost_conf_path, 'r') as f: + config_content = f.read() + + # Check if fix is needed + if f'{master_domain}.error_log' not in config_content and f'{master_domain}.access_log' not in config_content: + logging.CyberCPLogFileWriter.writeToFile(f'{domain_name} already has correct log configuration') + return True + + if dry_run: + logging.CyberCPLogFileWriter.writeToFile(f'[DRY RUN] Would fix log paths for {domain_name}') + return True + + # Create backup if requested + if create_backup: + backup_path = f"{vhost_conf_path}.backup.{datetime.now().strftime('%Y%m%d_%H%M%S')}" + shutil.copy2(vhost_conf_path, backup_path) + logging.CyberCPLogFileWriter.writeToFile(f'Created backup: {backup_path}') + + # Fix the configuration + fixed_content = config_content + + # Fix error log path + fixed_content = re.sub( + rf'errorlog\s+\$VH_ROOT/logs/{re.escape(master_domain)}\.error_log', + f'errorlog $VH_ROOT/logs/{domain_name}.error_log', + fixed_content + ) + + # Fix access log path + fixed_content = re.sub( + rf'accesslog\s+\$VH_ROOT/logs/{re.escape(master_domain)}\.access_log', + f'accesslog $VH_ROOT/logs/{domain_name}.access_log', + fixed_content + ) + + # Fix CustomLog paths (for Apache configurations) + fixed_content = re.sub( + rf'CustomLog\s+/home/{re.escape(master_domain)}/logs/{re.escape(master_domain)}\.access_log', + f'CustomLog /home/{domain_name}/logs/{domain_name}.access_log', + fixed_content + ) + + # Write the fixed configuration + with open(vhost_conf_path, 'w') as f: + f.write(fixed_content) + + # Set proper ownership + ProcessUtilities.executioner(f'chown lsadm:lsadm {vhost_conf_path}') + + # Create the log directory if it doesn't exist + log_dir = f"/home/{master_domain}/logs" + if not os.path.exists(log_dir): + os.makedirs(log_dir, exist_ok=True) + ProcessUtilities.executioner(f'chown -R {child_domain.master.externalApp}:{child_domain.master.externalApp} {log_dir}') + + # Create separate log files for the child domain + error_log_path = f"{log_dir}/{domain_name}.error_log" + access_log_path = f"{log_dir}/{domain_name}.access_log" + + # Create empty log files if they don't exist + for log_path in [error_log_path, access_log_path]: + if not os.path.exists(log_path): + with open(log_path, 'w') as f: + f.write('') + ProcessUtilities.executioner(f'chown {child_domain.master.externalApp}:{child_domain.master.externalApp} {log_path}') + ProcessUtilities.executioner(f'chmod 644 {log_path}') + + # Restart LiteSpeed to apply changes + ProcessUtilities.executioner('systemctl restart lsws') + + logging.CyberCPLogFileWriter.writeToFile(f'Fixed subdomain log configuration for {domain_name}') + return True + + except Exception as e: + logging.CyberCPLogFileWriter.writeToFile(f'Error fixing subdomain logs for {domain_name}: {str(e)}') + return False +