mirror of
				https://github.com/usmannasir/cyberpanel.git
				synced 2025-10-31 18:36:17 +01:00 
			
		
		
		
	
		
			
	
	
		
			1532 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			1532 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|  | #!/usr/local/CyberCP/bin/python | ||
|  | import json | ||
|  | import os | ||
|  | import sys | ||
|  | import time | ||
|  | from random import randint | ||
|  | import socket | ||
|  | import shutil | ||
|  | import docker | ||
|  | 
 | ||
|  | sys.path.append('/usr/local/CyberCP') | ||
|  | 
 | ||
|  | try: | ||
|  |     import django | ||
|  | except: | ||
|  |     pass | ||
|  | 
 | ||
|  | try: | ||
|  |     from plogical import randomPassword | ||
|  |     from plogical.acl import ACLManager | ||
|  |     from dockerManager.dockerInstall import DockerInstall | ||
|  | except: | ||
|  |     pass | ||
|  | 
 | ||
|  | from plogical.processUtilities import ProcessUtilities | ||
|  | from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging | ||
|  | import argparse | ||
|  | import threading as multi | ||
|  | 
 | ||
|  | class DockerDeploymentError(Exception): | ||
|  |     def __init__(self, message, error_code=None, recovery_possible=True): | ||
|  |         self.message = message | ||
|  |         self.error_code = error_code | ||
|  |         self.recovery_possible = recovery_possible | ||
|  |         super().__init__(self.message) | ||
|  | 
 | ||
|  | class Docker_Sites(multi.Thread): | ||
|  |     Wordpress = 1 | ||
|  |     Joomla = 2 | ||
|  | 
 | ||
|  |     # Error codes | ||
|  |     ERROR_DOCKER_NOT_INSTALLED = 'DOCKER_NOT_INSTALLED' | ||
|  |     ERROR_PORT_IN_USE = 'PORT_IN_USE' | ||
|  |     ERROR_CONTAINER_FAILED = 'CONTAINER_FAILED' | ||
|  |     ERROR_NETWORK_FAILED = 'NETWORK_FAILED' | ||
|  |     ERROR_VOLUME_FAILED = 'VOLUME_FAILED' | ||
|  |     ERROR_DB_FAILED = 'DB_FAILED' | ||
|  | 
 | ||
|  |     def __init__(self, function_run, data): | ||
|  |         multi.Thread.__init__(self) | ||
|  |         self.function_run = function_run | ||
|  |         self.data = data | ||
|  |         try: | ||
|  |             self.JobID = self.data['JobID']  ##JOBID will be file path where status is being written | ||
|  |         except: | ||
|  |             pass | ||
|  |         try: | ||
|  |             ### set docker name for listing/deleting etc | ||
|  |             if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8: | ||
|  |                 self.DockerAppName = f"{self.data['name'].replace(' ', '')}-{self.data['name'].replace(' ', '-')}" | ||
|  |             else: | ||
|  |                 self.DockerAppName = f"{self.data['name'].replace(' ', '')}_{self.data['name'].replace(' ', '-')}" | ||
|  |         except: | ||
|  |             pass | ||
|  | 
 | ||
|  |         command = 'cat /etc/csf/csf.conf' | ||
|  |         result = ProcessUtilities.outputExecutioner(command) | ||
|  | 
 | ||
|  |         if result.find('SECTION:Initial Settings') > -1: | ||
|  | 
 | ||
|  |             from plogical.csf import CSF | ||
|  |             from plogical.virtualHostUtilities import virtualHostUtilities | ||
|  |             currentSettings = CSF.fetchCSFSettings() | ||
|  | 
 | ||
|  |             tcpIN = currentSettings['tcpIN'] | ||
|  | 
 | ||
|  |             if os.path.exists(ProcessUtilities.debugPath): | ||
|  |                 logging.writeToFile(f'TCPIN docker: {tcpIN}') | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  |             if tcpIN.find('8088') == -1: | ||
|  | 
 | ||
|  |                 ports = f'{tcpIN},8088' | ||
|  | 
 | ||
|  |                 portsPath = '/home/cyberpanel/' + str(randint(1000, 9999)) | ||
|  | 
 | ||
|  |                 if os.path.exists(portsPath): | ||
|  |                     os.remove(portsPath) | ||
|  | 
 | ||
|  |                 writeToFile = open(portsPath, 'w') | ||
|  |                 writeToFile.write(ports) | ||
|  |                 writeToFile.close() | ||
|  | 
 | ||
|  |                 execPath = "sudo /usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/csf.py" | ||
|  |                 execPath = execPath + f" modifyPorts --protocol TCP_IN --ports " + portsPath | ||
|  |                 ProcessUtilities.executioner(execPath) | ||
|  | 
 | ||
|  |             tcpOUT = currentSettings['tcpOUT'] | ||
|  |             if tcpOUT.find('8088') == -1: | ||
|  | 
 | ||
|  |                 ports = f'{tcpOUT},8088' | ||
|  | 
 | ||
|  |                 portsPath = '/home/cyberpanel/' + str(randint(1000, 9999)) | ||
|  | 
 | ||
|  |                 if os.path.exists(portsPath): | ||
|  |                     os.remove(portsPath) | ||
|  | 
 | ||
|  |                 writeToFile = open(portsPath, 'w') | ||
|  |                 writeToFile.write(ports) | ||
|  |                 writeToFile.close() | ||
|  | 
 | ||
|  |                 execPath = "sudo /usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/csf.py" | ||
|  |                 execPath = execPath + f" modifyPorts --protocol TCP_OUT --ports " + portsPath | ||
|  |                 ProcessUtilities.executioner(execPath) | ||
|  | 
 | ||
|  | 
 | ||
|  |     def run(self): | ||
|  |         try: | ||
|  |             if self.function_run == 'DeployWPContainer': | ||
|  |                 self.DeployWPContainer() | ||
|  |             elif self.function_run == 'SubmitDockersiteCreation': | ||
|  |                 self.SubmitDockersiteCreation() | ||
|  |             elif self.function_run == 'DeployN8NContainer': | ||
|  |                 self.DeployN8NContainer() | ||
|  | 
 | ||
|  | 
 | ||
|  |         except BaseException as msg: | ||
|  |             logging.writeToFile(str(msg) + ' [Docker_Sites.run]') | ||
|  | 
 | ||
|  |     def InstallDocker(self): | ||
|  | 
 | ||
|  |         if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8: | ||
|  | 
 | ||
|  |             command = 'dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo' | ||
|  | 
 | ||
|  |             ReturnCode = ProcessUtilities.executioner(command) | ||
|  | 
 | ||
|  |             if ReturnCode: | ||
|  |                 pass | ||
|  |             else: | ||
|  |                 return 0, ReturnCode | ||
|  | 
 | ||
|  |             command = 'dnf install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y' | ||
|  |             ReturnCode = ProcessUtilities.executioner(command) | ||
|  | 
 | ||
|  |             if ReturnCode: | ||
|  |                 pass | ||
|  |             else: | ||
|  |                 return 0, ReturnCode | ||
|  | 
 | ||
|  |             command = 'systemctl enable docker' | ||
|  |             ReturnCode = ProcessUtilities.executioner(command) | ||
|  | 
 | ||
|  |             if ReturnCode: | ||
|  |                 pass | ||
|  |             else: | ||
|  |                 return 0, ReturnCode | ||
|  | 
 | ||
|  |             command = 'systemctl start docker' | ||
|  |             ReturnCode = ProcessUtilities.executioner(command) | ||
|  | 
 | ||
|  |             if ReturnCode: | ||
|  |                 pass | ||
|  |             else: | ||
|  |                 return 0, ReturnCode | ||
|  | 
 | ||
|  |             command = 'curl -L "https://github.com/docker/compose/releases/download/v2.23.2/docker-compose-linux-x86_64" -o /usr/bin/docker-compose' | ||
|  |             ReturnCode = ProcessUtilities.executioner(command, 'root', True) | ||
|  | 
 | ||
|  |             if ReturnCode: | ||
|  |                 pass | ||
|  |             else: | ||
|  |                 return 0, ReturnCode | ||
|  | 
 | ||
|  |             command = 'chmod +x /usr/bin/docker-compose' | ||
|  |             ReturnCode = ProcessUtilities.executioner(command, 'root', True) | ||
|  | 
 | ||
|  |             if ReturnCode: | ||
|  |                 return 1, None | ||
|  |             else: | ||
|  |                 return 0, ReturnCode | ||
|  | 
 | ||
|  |         else: | ||
|  |             # Add Docker's official GPG key | ||
|  |             command = 'curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg' | ||
|  |             ReturnCode = ProcessUtilities.executioner(command, 'root', True) | ||
|  |             if not ReturnCode: | ||
|  |                 return 0, ReturnCode | ||
|  | 
 | ||
|  |             # Add Docker repository | ||
|  |             command = 'echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null' | ||
|  |             ReturnCode = ProcessUtilities.executioner(command, 'root', True) | ||
|  |             if not ReturnCode: | ||
|  |                 return 0, ReturnCode | ||
|  | 
 | ||
|  |             # Update package index | ||
|  |             command = 'apt-get update' | ||
|  |             ReturnCode = ProcessUtilities.executioner(command) | ||
|  |             if not ReturnCode: | ||
|  |                 return 0, ReturnCode | ||
|  | 
 | ||
|  |             # Install Docker packages | ||
|  |             command = 'apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin' | ||
|  |             ReturnCode = ProcessUtilities.executioner(command) | ||
|  |             if not ReturnCode: | ||
|  |                 return 0, ReturnCode | ||
|  | 
 | ||
|  |             # Enable and start Docker service | ||
|  |             command = 'systemctl enable docker' | ||
|  |             ReturnCode = ProcessUtilities.executioner(command) | ||
|  |             if not ReturnCode: | ||
|  |                 return 0, ReturnCode | ||
|  | 
 | ||
|  |             command = 'systemctl start docker' | ||
|  |             ReturnCode = ProcessUtilities.executioner(command) | ||
|  |             if not ReturnCode: | ||
|  |                 return 0, ReturnCode | ||
|  | 
 | ||
|  |             # Install Docker Compose | ||
|  |             command = 'curl -L "https://github.com/docker/compose/releases/download/v2.23.2/docker-compose-linux-$(uname -m)" -o /usr/local/bin/docker-compose' | ||
|  |             ReturnCode = ProcessUtilities.executioner(command, 'root', True) | ||
|  |             if not ReturnCode: | ||
|  |                 return 0, ReturnCode | ||
|  | 
 | ||
|  |             command = 'chmod +x /usr/local/bin/docker-compose' | ||
|  |             ReturnCode = ProcessUtilities.executioner(command, 'root', True) | ||
|  |             if not ReturnCode: | ||
|  |                 return 0, ReturnCode | ||
|  | 
 | ||
|  |             return 1, None | ||
|  | 
 | ||
|  |     @staticmethod | ||
|  |     def SetupProxy(port): | ||
|  |         import xml.etree.ElementTree as ET | ||
|  | 
 | ||
|  |         if ProcessUtilities.decideServer() == ProcessUtilities.OLS: | ||
|  |             ConfPath = '/usr/local/lsws/conf/httpd_config.conf' | ||
|  |             data = open(ConfPath, 'r').read() | ||
|  |             StringCheck = f"127.0.0.1:{port}" | ||
|  |             if data.find(StringCheck) == -1: | ||
|  |                 ProxyContent = f"""
 | ||
|  | extprocessor docker{port} {{ | ||
|  |   type                    proxy | ||
|  |   address                 127.0.0.1:{port} | ||
|  |   maxConns                100 | ||
|  |   pcKeepAliveTimeout      3600 | ||
|  |   initTimeout             300 | ||
|  |   retryTimeout            0 | ||
|  |   respBuffer              0 | ||
|  | }}     | ||
|  | """
 | ||
|  | 
 | ||
|  |                 WriteToFile = open(ConfPath, 'a') | ||
|  |                 WriteToFile.write(ProxyContent) | ||
|  |                 WriteToFile.close() | ||
|  | 
 | ||
|  |         else: | ||
|  |             ConfPath = '/usr/local/lsws/conf/httpd_config.xml' | ||
|  |             data = open(ConfPath, 'r').read() | ||
|  | 
 | ||
|  |             # Parse the XML | ||
|  |             root = ET.fromstring(data) | ||
|  | 
 | ||
|  |             # Find the <extProcessorList> node | ||
|  |             ext_processor_list = root.find('extProcessorList') | ||
|  | 
 | ||
|  |             # Create the new <extProcessor> node | ||
|  |             new_ext_processor = ET.Element('extProcessor') | ||
|  | 
 | ||
|  |             # Add child elements to the new <extProcessor> | ||
|  |             ET.SubElement(new_ext_processor, 'type').text = 'proxy' | ||
|  |             ET.SubElement(new_ext_processor, 'name').text = f'docker{port}' | ||
|  |             ET.SubElement(new_ext_processor, 'address').text = f'127.0.0.1:{port}' | ||
|  |             ET.SubElement(new_ext_processor, 'maxConns').text = '100' | ||
|  |             ET.SubElement(new_ext_processor, 'pcKeepAliveTimeout').text = '3600' | ||
|  |             ET.SubElement(new_ext_processor, 'initTimeout').text = '300' | ||
|  |             ET.SubElement(new_ext_processor, 'retryTimeout').text = '0' | ||
|  |             ET.SubElement(new_ext_processor, 'respBuffer').text = '0' | ||
|  | 
 | ||
|  |             # Append the new <extProcessor> to the <extProcessorList> | ||
|  |             ext_processor_list.append(new_ext_processor) | ||
|  | 
 | ||
|  |             # Write the updated XML content to a new file or print it out | ||
|  |             tree = ET.ElementTree(root) | ||
|  |             tree.write(ConfPath, encoding='UTF-8', xml_declaration=True) | ||
|  | 
 | ||
|  |             # Optionally, print the updated XML | ||
|  |             ET.dump(root) | ||
|  | 
 | ||
|  | 
 | ||
|  |     @staticmethod | ||
|  |     def SetupN8NVhost(domain, port): | ||
|  |         """Setup n8n vhost with proper proxy configuration including Origin header""" | ||
|  |         try: | ||
|  |             vhost_path = f'/usr/local/lsws/conf/vhosts/{domain}/vhost.conf' | ||
|  |              | ||
|  |             if not os.path.exists(vhost_path): | ||
|  |                 logging.writeToFile(f"Error: Vhost file not found at {vhost_path}") | ||
|  |                 return False | ||
|  |              | ||
|  |             # Read existing vhost configuration | ||
|  |             with open(vhost_path, 'r') as f: | ||
|  |                 content = f.read() | ||
|  |              | ||
|  |             # Check if context already exists | ||
|  |             if 'context / {' in content: | ||
|  |                 logging.writeToFile("Context already exists, skipping...") | ||
|  |                 return True | ||
|  |              | ||
|  |             # Add proxy context with proper headers for n8n | ||
|  |             proxy_context = f'''
 | ||
|  | 
 | ||
|  | # N8N Proxy Configuration | ||
|  | context / {{ | ||
|  |   type                    proxy | ||
|  |   handler                 docker{port} | ||
|  |   addDefaultCharset       off | ||
|  |   websocket               1 | ||
|  | 
 | ||
|  |   extraHeaders            <<<END_extraHeaders | ||
|  |   RequestHeader set X-Forwarded-For $ip | ||
|  |   RequestHeader set X-Forwarded-Proto https | ||
|  |   RequestHeader set X-Forwarded-Host "{domain}" | ||
|  |   RequestHeader set Origin "{domain}, {domain}" | ||
|  |   RequestHeader set Host "{domain}" | ||
|  |   END_extraHeaders | ||
|  | }} | ||
|  | '''
 | ||
|  |              | ||
|  |             # Append at the end of file | ||
|  |             with open(vhost_path, 'a') as f: | ||
|  |                 f.write(proxy_context) | ||
|  |              | ||
|  |             logging.writeToFile(f"Successfully updated vhost for {domain}") | ||
|  |             return True | ||
|  |              | ||
|  |         except Exception as e: | ||
|  |             logging.writeToFile(f'Error setting up n8n vhost: {str(e)}') | ||
|  |             return False | ||
|  |      | ||
|  |     @staticmethod | ||
|  |     def SetupHTAccess(port, htaccess): | ||
|  |         ### Update htaccess | ||
|  | 
 | ||
|  |         StringCheck = f'docker{port}' | ||
|  | 
 | ||
|  |         try: | ||
|  |             Content = open(htaccess, 'r').read() | ||
|  |         except: | ||
|  |             Content = '' | ||
|  | 
 | ||
|  |         print(f'value of content {Content}') | ||
|  | 
 | ||
|  |         if Content.find(StringCheck) == -1: | ||
|  |             HTAccessContent = f'''
 | ||
|  | RewriteEngine On | ||
|  | REWRITERULE ^(.*)$ HTTP://docker{port}/$1 [P] | ||
|  | '''
 | ||
|  |             WriteToFile = open(htaccess, 'a') | ||
|  |             WriteToFile.write(HTAccessContent) | ||
|  |             WriteToFile.close() | ||
|  | 
 | ||
|  |     # Takes | ||
|  |     # ComposePath, MySQLPath, MySQLRootPass, MySQLDBName, MySQLDBNUser, MySQLPassword, CPUsMySQL, MemoryMySQL, | ||
|  |     # port, SitePath, CPUsSite, MemorySite, ComposePath, SiteName | ||
|  |     # finalURL, blogTitle, adminUser, adminPassword, adminEmail, htaccessPath, externalApp | ||
|  | 
 | ||
|  |     def DeployWPContainer(self): | ||
|  | 
 | ||
|  |         try: | ||
|  |             logging.statusWriter(self.JobID, 'Checking if Docker is installed..,0') | ||
|  | 
 | ||
|  |             command = 'docker --help' | ||
|  |             result = ProcessUtilities.outputExecutioner(command) | ||
|  | 
 | ||
|  |             if os.path.exists(ProcessUtilities.debugPath): | ||
|  |                 logging.writeToFile(f'return code of docker install {result}') | ||
|  | 
 | ||
|  |             if result.find("not found") > -1: | ||
|  |                 if os.path.exists(ProcessUtilities.debugPath): | ||
|  |                     logging.writeToFile(f'About to run docker install function...') | ||
|  | 
 | ||
|  |                 execPath = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/dockerManager/dockerInstall.py" | ||
|  |                 ProcessUtilities.executioner(execPath) | ||
|  | 
 | ||
|  |             logging.statusWriter(self.JobID, 'Docker is ready to use..,10') | ||
|  | 
 | ||
|  |             self.data['ServiceName'] = self.data["SiteName"].replace(' ', '-') | ||
|  | 
 | ||
|  |             WPSite = f'''
 | ||
|  | version: '3.8' | ||
|  | 
 | ||
|  | services: | ||
|  |   '{self.data['ServiceName']}': | ||
|  |     user: root | ||
|  |     image: cyberpanel/openlitespeed:latest | ||
|  |     ports: | ||
|  |       - "{self.data['port']}:8088" | ||
|  | #      - "443:443" | ||
|  |     environment: | ||
|  |       DB_NAME: "{self.data['MySQLDBName']}" | ||
|  |       DB_USER: "{self.data['MySQLDBNUser']}" | ||
|  |       DB_PASSWORD: "{self.data['MySQLPassword']}" | ||
|  |       WP_ADMIN_EMAIL: "{self.data['adminEmail']}" | ||
|  |       WP_ADMIN_USER: "{self.data['adminUser']}" | ||
|  |       WP_ADMIN_PASSWORD: "{self.data['adminPassword']}" | ||
|  |       WP_URL: {self.data['finalURL']} | ||
|  |       DB_Host: '{self.data['ServiceName']}-db:3306' | ||
|  |       SITE_NAME: '{self.data['SiteName']}' | ||
|  |     volumes: | ||
|  | #      - "/home/docker/{self.data['finalURL']}:/usr/local/lsws/Example/html" | ||
|  |       - "/home/docker/{self.data['finalURL']}/data:/usr/local/lsws/Example/html" | ||
|  |     depends_on: | ||
|  |       - '{self.data['ServiceName']}-db' | ||
|  |     deploy: | ||
|  |       resources: | ||
|  |         limits: | ||
|  |           cpus: '{self.data['CPUsSite']}'  # Use 50% of one CPU core | ||
|  |           memory: {self.data['MemorySite']}M  # Limit memory to 512 megabytes | ||
|  |   '{self.data['ServiceName']}-db': | ||
|  |     image: mariadb | ||
|  |     restart: always | ||
|  |     environment: | ||
|  | #      ALLOW_EMPTY_PASSWORD=no | ||
|  |       MYSQL_DATABASE: '{self.data['MySQLDBName']}' | ||
|  |       MYSQL_USER: '{self.data['MySQLDBNUser']}' | ||
|  |       MYSQL_PASSWORD: '{self.data['MySQLPassword']}' | ||
|  |       MYSQL_ROOT_PASSWORD: '{self.data['MySQLPassword']}' | ||
|  |     volumes: | ||
|  |       - "/home/docker/{self.data['finalURL']}/db:/var/lib/mysql" | ||
|  |     deploy: | ||
|  |       resources: | ||
|  |         limits: | ||
|  |           cpus: '{self.data['CPUsMySQL']}'  # Use 50% of one CPU core | ||
|  |           memory: {self.data['MemoryMySQL']}M  # Limit memory to 512 megabytes             | ||
|  | '''
 | ||
|  | 
 | ||
|  |             ### WriteConfig to compose-file | ||
|  | 
 | ||
|  |             command = f"mkdir -p /home/docker/{self.data['finalURL']}" | ||
|  |             result, message = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  | 
 | ||
|  |             if result == 0: | ||
|  |                 logging.statusWriter(self.JobID, f'Error {str(message)} . [404]') | ||
|  |                 return 0 | ||
|  | 
 | ||
|  |             TempCompose = f'/home/cyberpanel/{self.data["finalURL"]}-docker-compose.yml' | ||
|  | 
 | ||
|  |             WriteToFile = open(TempCompose, 'w') | ||
|  |             WriteToFile.write(WPSite) | ||
|  |             WriteToFile.close() | ||
|  | 
 | ||
|  |             command = f"mv {TempCompose} {self.data['ComposePath']}" | ||
|  |             result, message = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  | 
 | ||
|  |             if result == 0: | ||
|  |                 logging.statusWriter(self.JobID, f'Error {str(message)} . [404]') | ||
|  |                 return 0 | ||
|  | 
 | ||
|  |             command = f"chmod 600 {self.data['ComposePath']} && chown root:root {self.data['ComposePath']}" | ||
|  |             ProcessUtilities.executioner(command, 'root', True) | ||
|  | 
 | ||
|  |             #### | ||
|  | 
 | ||
|  |             if ProcessUtilities.decideDistro() == ProcessUtilities.cent8 or ProcessUtilities.decideDistro() == ProcessUtilities.centos: | ||
|  |                 dockerCommand = 'docker compose' | ||
|  |             else: | ||
|  |                 dockerCommand = 'docker-compose' | ||
|  | 
 | ||
|  |             command = f"{dockerCommand} -f {self.data['ComposePath']} -p '{self.data['SiteName']}' up -d" | ||
|  |             result, message = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  | 
 | ||
|  |             if os.path.exists(ProcessUtilities.debugPath): | ||
|  |                 logging.writeToFile(message) | ||
|  | 
 | ||
|  |             if result == 0: | ||
|  |                 logging.statusWriter(self.JobID, f'Error {str(message)} . [404]') | ||
|  |                 return 0 | ||
|  | 
 | ||
|  |             logging.statusWriter(self.JobID, 'Bringing containers online..,50') | ||
|  | 
 | ||
|  |             time.sleep(25) | ||
|  | 
 | ||
|  |             ### checking if everything ran properly | ||
|  | 
 | ||
|  |             passdata = {} | ||
|  |             passdata["JobID"] = None | ||
|  |             passdata['name'] = self.data['ServiceName'] | ||
|  |             da = Docker_Sites(None, passdata) | ||
|  |             retdata, containers = da.ListContainers() | ||
|  | 
 | ||
|  |             containers = json.loads(containers) | ||
|  | 
 | ||
|  |             if os.path.exists(ProcessUtilities.debugPath): | ||
|  |                 logging.writeToFile(str(containers)) | ||
|  | 
 | ||
|  |             ### it means less then two containers which means something went wrong | ||
|  |             if len(containers) < 2: | ||
|  |                 logging.writeToFile(f'Unkonwn error, containers not running. [DeployWPContainer]') | ||
|  |                 logging.statusWriter(self.JobID, f'Unkonwn error, containers not running. [DeployWPContainer]') | ||
|  |                 return 0 | ||
|  | 
 | ||
|  |             ### Set up Proxy | ||
|  | 
 | ||
|  |             execPath = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/DockerSites.py" | ||
|  |             execPath = execPath + f" SetupProxy --port {self.data['port']}" | ||
|  |             ProcessUtilities.executioner(execPath) | ||
|  | 
 | ||
|  |             ### Set up ht access | ||
|  | 
 | ||
|  |             execPath = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/DockerSites.py" | ||
|  |             execPath = execPath + f" SetupHTAccess --port {self.data['port']} --htaccess {self.data['htaccessPath']}" | ||
|  |             ProcessUtilities.executioner(execPath, self.data['externalApp']) | ||
|  | 
 | ||
|  |             if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8: | ||
|  |                 group = 'nobody' | ||
|  |             else: | ||
|  |                 group = 'nogroup' | ||
|  | 
 | ||
|  |             command = f"chown -R nobody:{group} /home/docker/{self.data['finalURL']}/data" | ||
|  |             ProcessUtilities.executioner(command) | ||
|  | 
 | ||
|  |             ### just restart ls for htaccess | ||
|  | 
 | ||
|  |             from plogical.installUtilities import installUtilities | ||
|  |             installUtilities.reStartLiteSpeedSocket() | ||
|  | 
 | ||
|  |             logging.statusWriter(self.JobID, 'Completed. [200]') | ||
|  | 
 | ||
|  |             # command = f"docker-compose -f {self.data['ComposePath']} ps -q wordpress" | ||
|  |             # stdout = ProcessUtilities.outputExecutioner(command) | ||
|  |             # | ||
|  |             # self.ContainerID = stdout.rstrip('\n') | ||
|  | 
 | ||
|  |             # command = f'docker-compose -f {self.data["ComposePath"]} exec {self.ContainerID} curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar' | ||
|  |             # result = ProcessUtilities.outputExecutioner(command) | ||
|  |             # | ||
|  |             # if os.path.exists(ProcessUtilities.debugPath): | ||
|  |             #     logging.writeToFile(result) | ||
|  |             # | ||
|  |             # command = f"docker-compose -f {self.data['ComposePath']} exec {self.ContainerID} chmod + wp-cli.phar" | ||
|  |             # result = ProcessUtilities.outputExecutioner(command) | ||
|  |             # | ||
|  |             # if os.path.exists(ProcessUtilities.debugPath): | ||
|  |             #     logging.writeToFile(result) | ||
|  |             # | ||
|  |             # command = f"docker-compose -f {self.data['ComposePath']} exec {self.ContainerID} mv wp-cli.phar /bin/wp" | ||
|  |             # result = ProcessUtilities.outputExecutioner(command) | ||
|  |             # | ||
|  |             # if os.path.exists(ProcessUtilities.debugPath): | ||
|  |             #     logging.writeToFile(result) | ||
|  | 
 | ||
|  |             # command = f'docker-compose -f {self.data["ComposePath"]} exec {self.ContainerID} wp core install --url="http://{self.data["finalURL"]}" --title="{self.data["blogTitle"]}" --admin_user="{self.data["adminUser"]}" --admin_password="{self.data["adminPassword"]}" --admin_email="{self.data["adminEmail"]}" --path=. --allow-root' | ||
|  |             # result = ProcessUtilities.outputExecutioner(command) | ||
|  |             # | ||
|  |             # if os.path.exists(ProcessUtilities.debugPath): | ||
|  |             #     logging.writeToFile(result) | ||
|  | 
 | ||
|  |         except BaseException as msg: | ||
|  |             logging.writeToFile(f'{str(msg)}. [DeployWPContainer]') | ||
|  |             logging.statusWriter(self.JobID, f'Error {str(msg)} . [404]') | ||
|  |             print(str(msg)) | ||
|  |             pass | ||
|  | 
 | ||
|  |     def SubmitDockersiteCreation(self): | ||
|  |         try: | ||
|  | 
 | ||
|  |             from websiteFunctions.models import DockerSites, Websites | ||
|  |             from websiteFunctions.website import WebsiteManager | ||
|  | 
 | ||
|  |             tempStatusPath = self.data['JobID'] | ||
|  |             statusFile = open(tempStatusPath, 'w') | ||
|  |             statusFile.writelines('Creating Website...,10') | ||
|  |             statusFile.close() | ||
|  | 
 | ||
|  |             Domain = self.data['Domain'] | ||
|  |             WPemal = self.data['WPemal'] | ||
|  |             Owner = self.data['Owner'] | ||
|  |             userID = self.data['userID'] | ||
|  |             MysqlCPU = self.data['MysqlCPU'] | ||
|  |             MYsqlRam = self.data['MYsqlRam'] | ||
|  |             SiteCPU = self.data['SiteCPU'] | ||
|  |             SiteRam = self.data['SiteRam'] | ||
|  |             sitename = self.data['sitename'] | ||
|  |             WPusername = self.data['WPusername'] | ||
|  |             WPpasswd = self.data['WPpasswd'] | ||
|  |             externalApp = self.data['externalApp'] | ||
|  | 
 | ||
|  |             currentTemp = tempStatusPath | ||
|  | 
 | ||
|  |             DataToPass = {} | ||
|  |             DataToPass['tempStatusPath'] = tempStatusPath | ||
|  |             DataToPass['domainName'] = Domain | ||
|  |             DataToPass['adminEmail'] = WPemal | ||
|  |             DataToPass['phpSelection'] = "PHP 8.1" | ||
|  |             DataToPass['websiteOwner'] = Owner | ||
|  |             DataToPass['package'] = 'Default' | ||
|  |             DataToPass['ssl'] = 1 | ||
|  |             DataToPass['dkimCheck'] = 0 | ||
|  |             DataToPass['openBasedir'] = 0 | ||
|  |             DataToPass['mailDomain'] = 0 | ||
|  |             DataToPass['apacheBackend'] = 0 | ||
|  |             UserID = userID | ||
|  | 
 | ||
|  |             if Websites.objects.filter(domain=DataToPass['domainName']).count() == 0: | ||
|  |                 try: | ||
|  |                     website = Websites.objects.get(domain=DataToPass['domainName']) | ||
|  | 
 | ||
|  |                     if website.phpSelection == 'PHP 7.3': | ||
|  |                         website.phpSelection = 'PHP 8.0' | ||
|  |                         website.save() | ||
|  | 
 | ||
|  |                     if ACLManager.checkOwnership(website.domain, self.data['adminID'], | ||
|  |                                                  self.data['currentACL']) == 0: | ||
|  |                         statusFile = open(tempStatusPath, 'w') | ||
|  |                         statusFile.writelines('You dont own this site.[404]') | ||
|  |                         statusFile.close() | ||
|  |                 except: | ||
|  | 
 | ||
|  |                     ab = WebsiteManager() | ||
|  |                     coreResult = ab.submitWebsiteCreation(UserID, DataToPass) | ||
|  |                     coreResult1 = json.loads((coreResult).content) | ||
|  |                     logging.writeToFile("Creating website result....%s" % coreResult1) | ||
|  |                     reutrntempath = coreResult1['tempStatusPath'] | ||
|  |                     while (1): | ||
|  |                         lastLine = open(reutrntempath, 'r').read() | ||
|  |                         logging.writeToFile("Error web creating lastline ....... %s" % lastLine) | ||
|  |                         if lastLine.find('[200]') > -1: | ||
|  |                             break | ||
|  |                         elif lastLine.find('[404]') > -1: | ||
|  |                             statusFile = open(currentTemp, 'w') | ||
|  |                             statusFile.writelines('Failed to Create Website: error: %s. [404]' % lastLine) | ||
|  |                             statusFile.close() | ||
|  |                             return 0 | ||
|  |                         else: | ||
|  |                             statusFile = open(currentTemp, 'w') | ||
|  |                             statusFile.writelines('Creating Website....,20') | ||
|  |                             statusFile.close() | ||
|  |                             time.sleep(2) | ||
|  | 
 | ||
|  |                     statusFile = open(tempStatusPath, 'w') | ||
|  |                     statusFile.writelines('Creating DockerSite....,30') | ||
|  |                     statusFile.close() | ||
|  | 
 | ||
|  |             webobj = Websites.objects.get(domain=Domain) | ||
|  | 
 | ||
|  |             if webobj.dockersites_set.all().count() > 0: | ||
|  |                 logging.statusWriter(self.JobID, f'Docker container already exists on this domain. [404]') | ||
|  |                 return 0 | ||
|  | 
 | ||
|  |             dbname = randomPassword.generate_pass() | ||
|  |             dbpasswd = randomPassword.generate_pass() | ||
|  |             dbusername = randomPassword.generate_pass() | ||
|  |             MySQLRootPass = randomPassword.generate_pass() | ||
|  | 
 | ||
|  |             if DockerSites.objects.count() == 0: | ||
|  |                 port = '11000' | ||
|  |             else: | ||
|  |                 port = str(int(DockerSites.objects.last().port) + 1) | ||
|  | 
 | ||
|  |             f_data = { | ||
|  |                 "JobID": tempStatusPath, | ||
|  |                 "ComposePath": f"/home/docker/{Domain}/docker-compose.yml", | ||
|  |                 "MySQLPath": f'/home/{Domain}/public_html/sqldocker', | ||
|  |                 "MySQLRootPass": MySQLRootPass, | ||
|  |                 "MySQLDBName": dbname, | ||
|  |                 "MySQLDBNUser": dbusername, | ||
|  |                 "MySQLPassword": dbpasswd, | ||
|  |                 "CPUsMySQL": MysqlCPU, | ||
|  |                 "MemoryMySQL": MYsqlRam, | ||
|  |                 "port": port, | ||
|  |                 "SitePath": f'/home/{Domain}/public_html/wpdocker', | ||
|  |                 "CPUsSite": SiteCPU, | ||
|  |                 "MemorySite": SiteRam, | ||
|  |                 "SiteName": sitename, | ||
|  |                 "finalURL": Domain, | ||
|  |                 "blogTitle": sitename, | ||
|  |                 "adminUser": WPusername, | ||
|  |                 "adminPassword": WPpasswd, | ||
|  |                 "adminEmail": WPemal, | ||
|  |                 "htaccessPath": f'/home/{Domain}/public_html/.htaccess', | ||
|  |                 "externalApp": webobj.externalApp, | ||
|  |                 "docRoot": f"/home/{Domain}" | ||
|  |             } | ||
|  | 
 | ||
|  |             dockersiteobj = DockerSites( | ||
|  |                 admin=webobj, ComposePath=f"/home/{Domain}/docker-compose.yml", | ||
|  |                 SitePath=f'/home/{Domain}/public_html/wpdocker', | ||
|  |                 MySQLPath=f'/home/{Domain}/public_html/sqldocker', SiteType=Docker_Sites.Wordpress, MySQLDBName=dbname, | ||
|  |                 MySQLDBNUser=dbusername, CPUsMySQL=MysqlCPU, MemoryMySQL=MYsqlRam, port=port, CPUsSite=SiteCPU, | ||
|  |                 MemorySite=SiteRam, | ||
|  |                 SiteName=sitename, finalURL=Domain, blogTitle=sitename, adminUser=WPusername, adminEmail=WPemal | ||
|  |             ) | ||
|  |             dockersiteobj.save() | ||
|  | 
 | ||
|  |             if self.data['App'] == 'WordPress': | ||
|  |                 background = Docker_Sites('DeployWPContainer', f_data) | ||
|  |                 background.start() | ||
|  |             elif self.data['App'] == 'n8n': | ||
|  |                 background = Docker_Sites('DeployN8NContainer', f_data) | ||
|  |                 background.start() | ||
|  | 
 | ||
|  |         except BaseException as msg: | ||
|  |             logging.writeToFile("Error Submit Docker site Creation ....... %s" % str(msg)) | ||
|  |             return 0 | ||
|  | 
 | ||
|  |     def DeleteDockerApp(self): | ||
|  |         try: | ||
|  | 
 | ||
|  |             command = f'docker-compose -f /home/docker/{self.data["domain"]}/docker-compose.yml down' | ||
|  |             ProcessUtilities.executioner(command) | ||
|  | 
 | ||
|  |             command = f'rm -rf /home/docker/{self.data["domain"]}' | ||
|  |             ProcessUtilities.executioner(command) | ||
|  | 
 | ||
|  |             command = f'rm -f /home/{self.data["domain"]}/public_html/.htaccess' | ||
|  |             ProcessUtilities.executioner(command) | ||
|  | 
 | ||
|  | 
 | ||
|  |             ### forcefully delete containers | ||
|  | 
 | ||
|  |             # Create a Docker client | ||
|  |             client = docker.from_env() | ||
|  | 
 | ||
|  |             FilerValue = self.DockerAppName | ||
|  | 
 | ||
|  |             # Define the label to filter containers | ||
|  |             label_filter = {'name': FilerValue} | ||
|  | 
 | ||
|  |             # List containers matching the label filter | ||
|  |             containers = client.containers.list(filters=label_filter) | ||
|  | 
 | ||
|  |             logging.writeToFile(f'List of containers {str(containers)}') | ||
|  | 
 | ||
|  | 
 | ||
|  |             for container in containers: | ||
|  |                 command = f'docker stop {container.short_id}' | ||
|  |                 ProcessUtilities.executioner(command) | ||
|  | 
 | ||
|  |                 command = f'docker rm {container.short_id}' | ||
|  |                 ProcessUtilities.executioner(command) | ||
|  | 
 | ||
|  | 
 | ||
|  |             command = f"rm -rf /home/{self.data['domain']}/public_html/.htaccess'" | ||
|  |             ProcessUtilities.executioner(command) | ||
|  | 
 | ||
|  |             from plogical.installUtilities import installUtilities | ||
|  |             installUtilities.reStartLiteSpeed() | ||
|  | 
 | ||
|  |         except BaseException as msg: | ||
|  |             logging.writeToFile("Error Delete Docker APP ....... %s" % str(msg)) | ||
|  |             return 0 | ||
|  | 
 | ||
|  |     ## This function need site name which was passed while creating the app | ||
|  |     def ListContainers(self): | ||
|  |         try: | ||
|  |             # Create a Docker client | ||
|  |             client = docker.from_env() | ||
|  | 
 | ||
|  |             # Debug logging | ||
|  |             if os.path.exists(ProcessUtilities.debugPath): | ||
|  |                 logging.writeToFile(f'DockerAppName: {self.DockerAppName}') | ||
|  | 
 | ||
|  |             # List all containers without filtering first | ||
|  |             all_containers = client.containers.list(all=True) | ||
|  |              | ||
|  |             if os.path.exists(ProcessUtilities.debugPath): | ||
|  |                 logging.writeToFile(f'Total containers found: {len(all_containers)}') | ||
|  |                 for container in all_containers: | ||
|  |                     logging.writeToFile(f'Container name: {container.name}') | ||
|  | 
 | ||
|  |             # Now filter containers - handle both CentOS and Ubuntu naming | ||
|  |             containers = [] | ||
|  |              | ||
|  |             # Get both possible name formats | ||
|  |             if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8: | ||
|  |                 search_name = self.DockerAppName  # Already in hyphen format for CentOS | ||
|  |             else: | ||
|  |                 # For Ubuntu, convert underscore to hyphen as containers use hyphens | ||
|  |                 search_name = self.DockerAppName.replace('_', '-') | ||
|  |              | ||
|  |             if os.path.exists(ProcessUtilities.debugPath): | ||
|  |                 logging.writeToFile(f'Searching for containers with name containing: {search_name}') | ||
|  | 
 | ||
|  |             for container in all_containers: | ||
|  |                 if os.path.exists(ProcessUtilities.debugPath): | ||
|  |                     logging.writeToFile(f'Checking container: {container.name} against filter: {search_name}') | ||
|  |                 if search_name.lower() in container.name.lower(): | ||
|  |                     containers.append(container) | ||
|  | 
 | ||
|  |             if os.path.exists(ProcessUtilities.debugPath): | ||
|  |                 logging.writeToFile(f'Filtered containers count: {len(containers)}') | ||
|  | 
 | ||
|  |             json_data = "[" | ||
|  |             checker = 0 | ||
|  | 
 | ||
|  |             for container in containers: | ||
|  |                 try: | ||
|  |                     dic = { | ||
|  |                         'id': container.short_id, | ||
|  |                         'name': container.name, | ||
|  |                         'status': container.status, | ||
|  |                         'state': container.attrs.get('State', {}), | ||
|  |                         'health': container.attrs.get('State', {}).get('Health', {}).get('Status', 'unknown'), | ||
|  |                         'volumes': container.attrs['HostConfig']['Binds'] if 'HostConfig' in container.attrs else [], | ||
|  |                         'logs_50': container.logs(tail=50).decode('utf-8'), | ||
|  |                         'ports': container.attrs['HostConfig']['PortBindings'] if 'HostConfig' in container.attrs else {} | ||
|  |                     } | ||
|  | 
 | ||
|  |                     if checker == 0: | ||
|  |                         json_data = json_data + json.dumps(dic) | ||
|  |                         checker = 1 | ||
|  |                     else: | ||
|  |                         json_data = json_data + ',' + json.dumps(dic) | ||
|  |                 except Exception as e: | ||
|  |                     logging.writeToFile(f"Error processing container {container.name}: {str(e)}") | ||
|  |                     continue | ||
|  | 
 | ||
|  |             json_data = json_data + ']' | ||
|  | 
 | ||
|  |             if os.path.exists(ProcessUtilities.debugPath): | ||
|  |                 logging.writeToFile(f'Final JSON data: {json_data}') | ||
|  | 
 | ||
|  |             return 1, json_data | ||
|  | 
 | ||
|  |         except BaseException as msg: | ||
|  |             logging.writeToFile("List Container ....... %s" % str(msg)) | ||
|  |             return 0, str(msg) | ||
|  | 
 | ||
|  |     ### pass container id and number of lines to fetch from logs | ||
|  |     def ContainerLogs(self): | ||
|  |         try: | ||
|  |             # Create a Docker client | ||
|  |             client = docker.from_env() | ||
|  | 
 | ||
|  |             # Get the container by ID | ||
|  |             container = client.containers.get(self.data['containerID']) | ||
|  | 
 | ||
|  |             # Fetch last 'tail' logs for the container | ||
|  |             logs = container.logs(tail=self.data['numberOfLines']).decode('utf-8') | ||
|  | 
 | ||
|  |             return 1, logs | ||
|  |         except BaseException as msg: | ||
|  |             logging.writeToFile("List Container ....... %s" % str(msg)) | ||
|  |             return 0, str(msg) | ||
|  | 
 | ||
|  |         ### pass container id and number of lines to fetch from logs | ||
|  | 
 | ||
|  |     def ContainerInfo(self): | ||
|  |         try: | ||
|  |             # Create a Docker client | ||
|  |             client = docker.from_env() | ||
|  | 
 | ||
|  |             # Get the container by ID | ||
|  |             container = client.containers.get(self.data['containerID']) | ||
|  | 
 | ||
|  |             # Fetch container stats | ||
|  |             stats = container.stats(stream=False) | ||
|  | 
 | ||
|  |             dic = { | ||
|  |                 'id': container.short_id, | ||
|  |                 'name': container.name, | ||
|  |                 'status': container.status, | ||
|  |                 'volumes': container.attrs['HostConfig']['Binds'] if 'HostConfig' in container.attrs else [], | ||
|  |                 'logs_50': container.logs(tail=50).decode('utf-8'), | ||
|  |                 'ports': container.attrs['HostConfig']['PortBindings'] if 'HostConfig' in container.attrs else {}, | ||
|  |                 'memory': stats['memory_stats']['usage'], | ||
|  |                 'cpu' : stats['cpu_stats']['cpu_usage']['total_usage'] | ||
|  |             } | ||
|  | 
 | ||
|  |             return 1, dic | ||
|  |         except BaseException as msg: | ||
|  |             logging.writeToFile("List Container ....... %s" % str(msg)) | ||
|  |             return 0, str(msg) | ||
|  | 
 | ||
|  |     def RebuildApp(self): | ||
|  |         self.DeleteDockerApp() | ||
|  |         self.SubmitDockersiteCreation() | ||
|  | 
 | ||
|  |     def RestartContainer(self): | ||
|  |         try: | ||
|  |             # Create a Docker client | ||
|  |             client = docker.from_env() | ||
|  | 
 | ||
|  |             # Get the container by ID | ||
|  |             container = client.containers.get(self.data['containerID']) | ||
|  | 
 | ||
|  |             container.restart() | ||
|  | 
 | ||
|  |             return 1, None | ||
|  |         except BaseException as msg: | ||
|  |             logging.writeToFile("List Container ....... %s" % str(msg)) | ||
|  |             return 0, str(msg) | ||
|  | 
 | ||
|  |     def StopContainer(self): | ||
|  |         try: | ||
|  |             # Create a Docker client | ||
|  |             client = docker.from_env() | ||
|  | 
 | ||
|  |             # Get the container by ID | ||
|  |             container = client.containers.get(self.data['containerID']) | ||
|  | 
 | ||
|  |             container.stop() | ||
|  | 
 | ||
|  |             return 1, None | ||
|  |         except BaseException as msg: | ||
|  |             logging.writeToFile("List Container ....... %s" % str(msg)) | ||
|  |             return 0, str(msg) | ||
|  | 
 | ||
|  |     ##### N8N Container | ||
|  | 
 | ||
|  |     def check_container_health(self, container_name, max_retries=3, delay=80): | ||
|  |         """
 | ||
|  |         Check if a container is running, accepting healthy, unhealthy, and starting states | ||
|  |         Total wait time will be 4 minutes (3 retries * 80 seconds) | ||
|  |         """
 | ||
|  |         try: | ||
|  |             # Format container name to match Docker's naming convention | ||
|  |             formatted_name = f"{self.data['ServiceName']}-{container_name}-1" | ||
|  |             logging.writeToFile(f'Checking container health for: {formatted_name}') | ||
|  |              | ||
|  |             for attempt in range(max_retries): | ||
|  |                 client = docker.from_env() | ||
|  |                 container = client.containers.get(formatted_name) | ||
|  |                  | ||
|  |                 if container.status == 'running': | ||
|  |                     health = container.attrs.get('State', {}).get('Health', {}).get('Status') | ||
|  |                      | ||
|  |                     # Accept healthy, unhealthy, and starting states as long as container is running | ||
|  |                     if health in ['healthy', 'unhealthy', 'starting'] or health is None: | ||
|  |                         logging.writeToFile(f'Container {formatted_name} is running with status: {health}') | ||
|  |                         return True | ||
|  |                     else: | ||
|  |                         health_logs = container.attrs.get('State', {}).get('Health', {}).get('Log', []) | ||
|  |                         if health_logs: | ||
|  |                             last_log = health_logs[-1] | ||
|  |                             logging.writeToFile(f'Container health check failed: {last_log.get("Output", "")}') | ||
|  |                  | ||
|  |                 logging.writeToFile(f'Container {formatted_name} status: {container.status}, health: {health}, attempt {attempt + 1}/{max_retries}') | ||
|  |                 time.sleep(delay) | ||
|  |                  | ||
|  |             return False | ||
|  |              | ||
|  |         except docker.errors.NotFound: | ||
|  |             logging.writeToFile(f'Container {formatted_name} not found') | ||
|  |             return False | ||
|  |         except Exception as e: | ||
|  |             logging.writeToFile(f'Error checking container health: {str(e)}') | ||
|  |             return False | ||
|  | 
 | ||
|  |     def verify_system_resources(self): | ||
|  |         try: | ||
|  |             # Check available disk space using root access | ||
|  |             command = "df -B 1G /home/docker --output=avail | tail -1" | ||
|  |             result, output = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  |             if result == 0: | ||
|  |                 raise DockerDeploymentError("Failed to check disk space") | ||
|  |             available_gb = int(output.strip()) | ||
|  | 
 | ||
|  |             if available_gb < 5:  # Require minimum 5GB free space | ||
|  |                 raise DockerDeploymentError( | ||
|  |                     f"Insufficient disk space. Need at least 5GB but only {available_gb}GB available.", | ||
|  |                     self.ERROR_VOLUME_FAILED | ||
|  |                 ) | ||
|  | 
 | ||
|  |             # Check if Docker is running and accessible | ||
|  |             command = "systemctl is-active docker" | ||
|  |             result, docker_status = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  |             if result == 0: | ||
|  |                 raise DockerDeploymentError("Failed to check Docker status") | ||
|  |             if docker_status.strip() != "active": | ||
|  |                 raise DockerDeploymentError("Docker service is not running") | ||
|  | 
 | ||
|  |             # Check Docker system info for resource limits | ||
|  |             command = "docker info --format '{{.MemTotal}}'" | ||
|  |             result, total_memory = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  |             if result == 0: | ||
|  |                 raise DockerDeploymentError("Failed to get Docker memory info") | ||
|  |              | ||
|  |             # Convert total_memory from bytes to MB | ||
|  |             total_memory_mb = int(total_memory.strip()) / (1024 * 1024) | ||
|  |              | ||
|  |             # Calculate required memory from site and MySQL requirements | ||
|  |             required_memory = int(self.data['MemoryMySQL']) + int(self.data['MemorySite']) | ||
|  |              | ||
|  |             if total_memory_mb < required_memory: | ||
|  |                 raise DockerDeploymentError( | ||
|  |                     f"Insufficient memory. Need {required_memory}MB but only {int(total_memory_mb)}MB available", | ||
|  |                     'INSUFFICIENT_MEMORY' | ||
|  |                 ) | ||
|  | 
 | ||
|  |             # Verify Docker group and permissions | ||
|  |             command = "getent group docker" | ||
|  |             result, docker_group = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  |             if result == 0 or not docker_group: | ||
|  |                 raise DockerDeploymentError("Docker group does not exist") | ||
|  | 
 | ||
|  |             return True | ||
|  | 
 | ||
|  |         except DockerDeploymentError as e: | ||
|  |             raise e | ||
|  |         except Exception as e: | ||
|  |             raise DockerDeploymentError(f"Resource verification failed: {str(e)}") | ||
|  | 
 | ||
|  |     def setup_docker_environment(self): | ||
|  |         try: | ||
|  |             # Create docker directory with root | ||
|  |             command = f"mkdir -p /home/docker/{self.data['finalURL']}" | ||
|  |             ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  | 
 | ||
|  |             # Set proper permissions | ||
|  |             command = f"chown -R {self.data['externalApp']}:docker /home/docker/{self.data['finalURL']}" | ||
|  |             ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  | 
 | ||
|  |             # Create docker network if doesn't exist | ||
|  |             command = "docker network ls | grep cyberpanel" | ||
|  |             network_exists = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  |             if not network_exists: | ||
|  |                 command = "docker network create cyberpanel" | ||
|  |                 ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  | 
 | ||
|  |             return True | ||
|  | 
 | ||
|  |         except Exception as e: | ||
|  |             raise DockerDeploymentError(f"Environment setup failed: {str(e)}") | ||
|  | 
 | ||
|  |     def deploy_containers(self): | ||
|  |         try: | ||
|  |             # Write docker-compose file | ||
|  |             command = f"cat > {self.data['ComposePath']} << 'EOF'\n{self.data['ComposeContent']}\nEOF" | ||
|  |             ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  | 
 | ||
|  |             # Set proper permissions on compose file | ||
|  |             command = f"chmod 600 {self.data['ComposePath']} && chown root:root {self.data['ComposePath']}" | ||
|  |             ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  | 
 | ||
|  |             # Deploy with docker-compose | ||
|  |             command = f"cd {os.path.dirname(self.data['ComposePath'])} && docker-compose up -d" | ||
|  |             result = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  | 
 | ||
|  |             if "error" in result.lower(): | ||
|  |                 raise DockerDeploymentError(f"Container deployment failed: {result}") | ||
|  | 
 | ||
|  |             return True | ||
|  | 
 | ||
|  |         except Exception as e: | ||
|  |             raise DockerDeploymentError(f"Deployment failed: {str(e)}") | ||
|  | 
 | ||
|  |     def cleanup_failed_deployment(self): | ||
|  |         try: | ||
|  |             # Stop and remove containers | ||
|  |             command = f"cd {os.path.dirname(self.data['ComposePath'])} && docker-compose down -v" | ||
|  |             ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  | 
 | ||
|  |             # Remove docker directory | ||
|  |             command = f"rm -rf /home/docker/{self.data['finalURL']}" | ||
|  |             ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  | 
 | ||
|  |             # Remove compose file | ||
|  |             command = f"rm -f {self.data['ComposePath']}" | ||
|  |             ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  | 
 | ||
|  |             return True | ||
|  | 
 | ||
|  |         except Exception as e: | ||
|  |             logging.writeToFile(f"Cleanup failed: {str(e)}") | ||
|  |             return False | ||
|  | 
 | ||
|  |     def monitor_deployment(self): | ||
|  |         try: | ||
|  |             # Format container names | ||
|  |             n8n_container_name = f"{self.data['ServiceName']}-{self.data['ServiceName']}-1" | ||
|  |             db_container_name = f"{self.data['ServiceName']}-{self.data['ServiceName']}-db-1" | ||
|  |              | ||
|  |             logging.writeToFile(f'Monitoring containers: {n8n_container_name} and {db_container_name}') | ||
|  | 
 | ||
|  |             # Check container health | ||
|  |             command = f"docker ps -a --filter name={self.data['ServiceName']} --format '{{{{.Status}}}}'" | ||
|  |             result, status = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  | 
 | ||
|  |             # Only raise error if container is exited | ||
|  |             if "exited" in status: | ||
|  |                 # Get container logs | ||
|  |                 command = f"docker logs {n8n_container_name}" | ||
|  |                 result, logs = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  |                 raise DockerDeploymentError(f"Container exited. Logs: {logs}") | ||
|  | 
 | ||
|  |             # Wait for database to be ready | ||
|  |             max_retries = 30 | ||
|  |             retry_count = 0 | ||
|  |             db_ready = False | ||
|  | 
 | ||
|  |             while retry_count < max_retries: | ||
|  |                 # Check if database container is ready | ||
|  |                 command = f"docker exec {db_container_name} pg_isready -U postgres" | ||
|  |                 result, output = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  |                  | ||
|  |                 if "accepting connections" in output: | ||
|  |                     db_ready = True | ||
|  |                     break | ||
|  |                  | ||
|  |                 # Check container status | ||
|  |                 command = f"docker inspect --format='{{{{.State.Status}}}}' {db_container_name}" | ||
|  |                 result, db_status = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  |                  | ||
|  |                 # Only raise error if database container is in a failed state | ||
|  |                 if db_status == 'exited': | ||
|  |                     raise DockerDeploymentError(f"Database container is in {db_status} state") | ||
|  |                  | ||
|  |                 retry_count += 1 | ||
|  |                 time.sleep(2) | ||
|  |                 logging.writeToFile(f'Waiting for database to be ready, attempt {retry_count}/{max_retries}') | ||
|  | 
 | ||
|  |             if not db_ready: | ||
|  |                 raise DockerDeploymentError("Database failed to become ready within timeout period") | ||
|  | 
 | ||
|  |             # Check n8n container status | ||
|  |             command = f"docker inspect --format='{{{{.State.Status}}}}' {n8n_container_name}" | ||
|  |             result, n8n_status = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  |              | ||
|  |             # Only raise error if n8n container is in a failed state | ||
|  |             if n8n_status == 'exited': | ||
|  |                 raise DockerDeploymentError(f"n8n container is in {n8n_status} state") | ||
|  | 
 | ||
|  |             logging.writeToFile(f'Deployment monitoring completed successfully. n8n status: {n8n_status}, database ready: {db_ready}') | ||
|  |             return True | ||
|  | 
 | ||
|  |         except Exception as e: | ||
|  |             logging.writeToFile(f'Error during monitoring: {str(e)}') | ||
|  |             raise DockerDeploymentError(f"Monitoring failed: {str(e)}") | ||
|  | 
 | ||
|  |     def handle_deployment_failure(self, error, cleanup=True): | ||
|  |         """
 | ||
|  |         Handle deployment failures and attempt recovery | ||
|  |         """
 | ||
|  |         try: | ||
|  |             logging.writeToFile(f'Deployment failed: {str(error)}') | ||
|  |              | ||
|  |             if cleanup: | ||
|  |                 self.cleanup_failed_deployment() | ||
|  |              | ||
|  |             if isinstance(error, DockerDeploymentError): | ||
|  |                 if error.error_code == self.ERROR_DOCKER_NOT_INSTALLED: | ||
|  |                     # Attempt to install Docker | ||
|  |                     execPath = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/dockerManager/dockerInstall.py" | ||
|  |                     ProcessUtilities.executioner(execPath) | ||
|  |                     return True | ||
|  |                      | ||
|  |                 elif error.error_code == self.ERROR_PORT_IN_USE: | ||
|  |                     # Find next available port | ||
|  |                     new_port = int(self.data['port']) + 1 | ||
|  |                     while new_port < 65535: | ||
|  |                         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
|  |                         result = sock.connect_ex(('127.0.0.1', new_port)) | ||
|  |                         sock.close() | ||
|  |                         if result != 0: | ||
|  |                             self.data['port'] = str(new_port) | ||
|  |                             return True | ||
|  |                         new_port += 1 | ||
|  |                          | ||
|  |                 elif error.error_code == self.ERROR_DB_FAILED: | ||
|  |                     # Attempt database recovery | ||
|  |                     return self.recover_database() | ||
|  |                      | ||
|  |             return False | ||
|  |              | ||
|  |         except Exception as e: | ||
|  |             logging.writeToFile(f'Error during failure handling: {str(e)}') | ||
|  |             return False | ||
|  | 
 | ||
|  |     def recover_database(self): | ||
|  |         """
 | ||
|  |         Attempt to recover the database container | ||
|  |         """
 | ||
|  |         try: | ||
|  |             client = docker.from_env() | ||
|  |             db_container_name = f"{self.data['ServiceName']}-db" | ||
|  |              | ||
|  |             try: | ||
|  |                 db_container = client.containers.get(db_container_name) | ||
|  |                  | ||
|  |                 if db_container.status == 'running': | ||
|  |                     exec_result = db_container.exec_run( | ||
|  |                         'pg_isready -U postgres' | ||
|  |                     ) | ||
|  |                      | ||
|  |                     if exec_result.exit_code != 0: | ||
|  |                         db_container.restart() | ||
|  |                         time.sleep(10) | ||
|  |                          | ||
|  |                         if self.check_container_health(db_container_name): | ||
|  |                             return True | ||
|  |                              | ||
|  |             except docker.errors.NotFound: | ||
|  |                 pass | ||
|  |                  | ||
|  |             return False | ||
|  |              | ||
|  |         except Exception as e: | ||
|  |             logging.writeToFile(f'Database recovery failed: {str(e)}') | ||
|  |             return False | ||
|  | 
 | ||
|  |     def log_deployment_metrics(self, metrics): | ||
|  |         """
 | ||
|  |         Log deployment metrics for analysis | ||
|  |         """
 | ||
|  |         if metrics: | ||
|  |             try: | ||
|  |                 log_file = f"/var/log/cyberpanel/docker/{self.data['ServiceName']}_metrics.json" | ||
|  |                 os.makedirs(os.path.dirname(log_file), exist_ok=True) | ||
|  |                  | ||
|  |                 with open(log_file, 'w') as f: | ||
|  |                     json.dump(metrics, f, indent=2) | ||
|  |                      | ||
|  |             except Exception as e: | ||
|  |                 logging.writeToFile(f'Error logging metrics: {str(e)}') | ||
|  | 
 | ||
|  |     def DeployN8NContainer(self): | ||
|  |         """
 | ||
|  |         Main deployment method with error handling | ||
|  |         """
 | ||
|  |         max_retries = 3 | ||
|  |         current_try = 0 | ||
|  |          | ||
|  |         while current_try < max_retries: | ||
|  |             try: | ||
|  |                 logging.statusWriter(self.JobID, 'Starting deployment verification...,0') | ||
|  |                  | ||
|  |                 # Check Docker installation | ||
|  |                 command = 'docker --help' | ||
|  |                 result = ProcessUtilities.outputExecutioner(command) | ||
|  |                 if result.find("not found") > -1: | ||
|  |                     if os.path.exists(ProcessUtilities.debugPath): | ||
|  |                         logging.writeToFile(f'About to run docker install function...') | ||
|  |                      | ||
|  |                     # Call InstallDocker to install Docker | ||
|  |                     install_result, error = self.InstallDocker() | ||
|  |                     if not install_result: | ||
|  |                         logging.statusWriter(self.JobID, f'Failed to install Docker: {error} [404]') | ||
|  |                         return 0 | ||
|  | 
 | ||
|  |                 logging.statusWriter(self.JobID, 'Docker installation verified...,20') | ||
|  | 
 | ||
|  |                 # Verify system resources | ||
|  |                 self.verify_system_resources() | ||
|  |                 logging.statusWriter(self.JobID, 'System resources verified...,10') | ||
|  | 
 | ||
|  |                 # Create directories | ||
|  |                 command = f"mkdir -p /home/docker/{self.data['finalURL']}" | ||
|  |                 result, message = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  |                 if result == 0: | ||
|  |                     raise DockerDeploymentError(f"Failed to create directories: {message}") | ||
|  |                 logging.statusWriter(self.JobID, 'Directories created...,30') | ||
|  | 
 | ||
|  |                 # Generate and write docker-compose file | ||
|  |                 self.data['ServiceName'] = self.data["SiteName"].replace(' ', '-') | ||
|  |                 compose_config = self.generate_compose_config() | ||
|  |                  | ||
|  |                 TempCompose = f'/home/cyberpanel/{self.data["finalURL"]}-docker-compose.yml' | ||
|  |                 with open(TempCompose, 'w') as f: | ||
|  |                     f.write(compose_config) | ||
|  |                  | ||
|  |                 command = f"mv {TempCompose} {self.data['ComposePath']}" | ||
|  |                 result, message = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  |                 if result == 0: | ||
|  |                     raise DockerDeploymentError(f"Failed to move compose file: {message}") | ||
|  |                  | ||
|  |                 command = f"chmod 600 {self.data['ComposePath']} && chown root:root {self.data['ComposePath']}" | ||
|  |                 ProcessUtilities.executioner(command, 'root', True) | ||
|  |                 logging.statusWriter(self.JobID, 'Docker compose file created...,40') | ||
|  | 
 | ||
|  |                 # Deploy containers | ||
|  |                 if ProcessUtilities.decideDistro() == ProcessUtilities.cent8 or ProcessUtilities.decideDistro() == ProcessUtilities.centos: | ||
|  |                     dockerCommand = 'docker compose' | ||
|  |                 else: | ||
|  |                     dockerCommand = 'docker-compose' | ||
|  | 
 | ||
|  |                 command = f"{dockerCommand} -f {self.data['ComposePath']} -p '{self.data['SiteName']}' up -d" | ||
|  |                 result, message = ProcessUtilities.outputExecutioner(command, None, None, None, 1) | ||
|  |                 if result == 0: | ||
|  |                     raise DockerDeploymentError(f"Failed to deploy containers: {message}") | ||
|  |                 logging.statusWriter(self.JobID, 'Containers deployed...,60') | ||
|  | 
 | ||
|  |                 # Wait for containers to be healthy | ||
|  |                 time.sleep(25) | ||
|  |                 if not self.check_container_health(f"{self.data['ServiceName']}-db") or \ | ||
|  |                    not self.check_container_health(self.data['ServiceName']): | ||
|  |                     raise DockerDeploymentError("Containers failed to reach healthy state", self.ERROR_CONTAINER_FAILED) | ||
|  |                 logging.statusWriter(self.JobID, 'Containers healthy...,70') | ||
|  | 
 | ||
|  |                 # Setup proxy | ||
|  |                 execPath = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/DockerSites.py" | ||
|  |                 execPath = execPath + f" SetupProxy --port {self.data['port']}" | ||
|  |                 ProcessUtilities.executioner(execPath) | ||
|  |                 logging.statusWriter(self.JobID, 'Proxy configured...,80') | ||
|  | 
 | ||
|  |                 # Setup n8n vhost configuration instead of htaccess | ||
|  |                 execPath = "/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/DockerSites.py" | ||
|  |                 execPath = execPath + f" SetupN8NVhost --domain {self.data['finalURL']} --port {self.data['port']}" | ||
|  |                 ProcessUtilities.executioner(execPath) | ||
|  |                 logging.statusWriter(self.JobID, 'N8N vhost configured...,90') | ||
|  | 
 | ||
|  |                 # Restart web server | ||
|  |                 from plogical.installUtilities import installUtilities | ||
|  |                 installUtilities.reStartLiteSpeedSocket() | ||
|  | 
 | ||
|  |                 # Monitor deployment | ||
|  |                 metrics = self.monitor_deployment() | ||
|  |                 self.log_deployment_metrics(metrics) | ||
|  | 
 | ||
|  |                 logging.statusWriter(self.JobID, 'Deployment completed successfully. [200]') | ||
|  |                 return True | ||
|  |                  | ||
|  |             except DockerDeploymentError as e: | ||
|  |                 logging.writeToFile(f'Deployment error: {str(e)}') | ||
|  |                  | ||
|  |                 if self.handle_deployment_failure(e): | ||
|  |                     current_try += 1 | ||
|  |                     continue | ||
|  |                 else: | ||
|  |                     logging.statusWriter(self.JobID, f'Deployment failed: {str(e)} [404]') | ||
|  |                     return False | ||
|  |                      | ||
|  |             except Exception as e: | ||
|  |                 logging.writeToFile(f'Unexpected error: {str(e)}') | ||
|  |                 self.handle_deployment_failure(e) | ||
|  |                 logging.statusWriter(self.JobID, f'Deployment failed: {str(e)} [404]') | ||
|  |                 return False | ||
|  |                  | ||
|  |         logging.statusWriter(self.JobID, f'Deployment failed after {max_retries} attempts [404]') | ||
|  |         return False | ||
|  | 
 | ||
|  |     def generate_compose_config(self): | ||
|  |         """
 | ||
|  |         Generate the docker-compose configuration with improved security and reliability | ||
|  |         """
 | ||
|  |         postgres_config = { | ||
|  |             'image': 'postgres:16-alpine', | ||
|  |             'user': 'root', | ||
|  |             'healthcheck': { | ||
|  |                 'test': ["CMD-SHELL", "pg_isready -U postgres"], | ||
|  |                 'interval': '10s', | ||
|  |                 'timeout': '5s', | ||
|  |                 'retries': 5, | ||
|  |                 'start_period': '30s' | ||
|  |             }, | ||
|  |             'environment': { | ||
|  |                 'POSTGRES_USER': 'postgres', | ||
|  |                 'POSTGRES_PASSWORD': self.data['MySQLPassword'], | ||
|  |                 'POSTGRES_DB': self.data['MySQLDBName'] | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         n8n_config = { | ||
|  |             'image': 'docker.n8n.io/n8nio/n8n', | ||
|  |             'user': 'root', | ||
|  |             'healthcheck': { | ||
|  |                 'test': ["CMD", "wget", "--spider", "http://localhost:5678"], | ||
|  |                 'interval': '20s', | ||
|  |                 'timeout': '10s', | ||
|  |                 'retries': 3 | ||
|  |             }, | ||
|  |             'environment': { | ||
|  |                 'DB_TYPE': 'postgresdb', | ||
|  |                 'DB_POSTGRESDB_HOST': f"{self.data['ServiceName']}-db", | ||
|  |                 'DB_POSTGRESDB_PORT': '5432', | ||
|  |                 'DB_POSTGRESDB_DATABASE': self.data['MySQLDBName'], | ||
|  |                 'DB_POSTGRESDB_USER': 'postgres', | ||
|  |                 'DB_POSTGRESDB_PASSWORD': self.data['MySQLPassword'], | ||
|  |                 'N8N_HOST': '0.0.0.0', | ||
|  |                 'N8N_PORT': '5678', | ||
|  |                 'NODE_ENV': 'production', | ||
|  |                 'N8N_EDITOR_BASE_URL': f"https://{self.data['finalURL']}", | ||
|  |                 'WEBHOOK_URL': f"https://{self.data['finalURL']}", | ||
|  |                 'WEBHOOK_TUNNEL_URL': f"https://{self.data['finalURL']}", | ||
|  |                 'N8N_PUSH_BACKEND': 'sse', | ||
|  |                 'GENERIC_TIMEZONE': 'UTC', | ||
|  |                 'N8N_ENCRYPTION_KEY': 'auto', | ||
|  |                 'N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS': 'true', | ||
|  |                 'DB_POSTGRESDB_SCHEMA': 'public', | ||
|  |                 'N8N_PROTOCOL': 'https', | ||
|  |                 'N8N_SECURE_COOKIE': 'true' | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         return f'''version: '3.8'
 | ||
|  | 
 | ||
|  | volumes: | ||
|  |   db_storage: | ||
|  |     driver: local | ||
|  |   n8n_storage: | ||
|  |     driver: local | ||
|  | 
 | ||
|  | services: | ||
|  |   '{self.data['ServiceName']}-db': | ||
|  |     image: {postgres_config['image']} | ||
|  |     user: {postgres_config['user']} | ||
|  |     restart: always | ||
|  |     healthcheck: | ||
|  |       test: {postgres_config['healthcheck']['test']} | ||
|  |       interval: {postgres_config['healthcheck']['interval']} | ||
|  |       timeout: {postgres_config['healthcheck']['timeout']} | ||
|  |       retries: {postgres_config['healthcheck']['retries']} | ||
|  |       start_period: {postgres_config['healthcheck']['start_period']} | ||
|  |     environment: | ||
|  |       - POSTGRES_USER={postgres_config['environment']['POSTGRES_USER']} | ||
|  |       - POSTGRES_PASSWORD={postgres_config['environment']['POSTGRES_PASSWORD']} | ||
|  |       - POSTGRES_DB={postgres_config['environment']['POSTGRES_DB']} | ||
|  |     volumes: | ||
|  |       - "/home/docker/{self.data['finalURL']}/db:/var/lib/postgresql/data" | ||
|  |     networks: | ||
|  |       - n8n-network | ||
|  |     deploy: | ||
|  |       resources: | ||
|  |         limits: | ||
|  |           cpus: '{self.data["CPUsMySQL"]}' | ||
|  |           memory: {self.data["MemoryMySQL"]}M | ||
|  | 
 | ||
|  |   '{self.data['ServiceName']}': | ||
|  |     image: {n8n_config['image']} | ||
|  |     user: {n8n_config['user']} | ||
|  |     restart: always | ||
|  |     healthcheck: | ||
|  |       test: {n8n_config['healthcheck']['test']} | ||
|  |       interval: {n8n_config['healthcheck']['interval']} | ||
|  |       timeout: {n8n_config['healthcheck']['timeout']} | ||
|  |       retries: {n8n_config['healthcheck']['retries']} | ||
|  |     environment: | ||
|  |       - DB_TYPE={n8n_config['environment']['DB_TYPE']} | ||
|  |       - DB_POSTGRESDB_HOST={n8n_config['environment']['DB_POSTGRESDB_HOST']} | ||
|  |       - DB_POSTGRESDB_PORT={n8n_config['environment']['DB_POSTGRESDB_PORT']} | ||
|  |       - DB_POSTGRESDB_DATABASE={n8n_config['environment']['DB_POSTGRESDB_DATABASE']} | ||
|  |       - DB_POSTGRESDB_USER={n8n_config['environment']['DB_POSTGRESDB_USER']} | ||
|  |       - DB_POSTGRESDB_PASSWORD={n8n_config['environment']['DB_POSTGRESDB_PASSWORD']} | ||
|  |       - DB_POSTGRESDB_SCHEMA={n8n_config['environment']['DB_POSTGRESDB_SCHEMA']} | ||
|  |       - N8N_HOST={n8n_config['environment']['N8N_HOST']} | ||
|  |       - N8N_PORT={n8n_config['environment']['N8N_PORT']} | ||
|  |       - NODE_ENV={n8n_config['environment']['NODE_ENV']} | ||
|  |       - N8N_EDITOR_BASE_URL={n8n_config['environment']['N8N_EDITOR_BASE_URL']} | ||
|  |       - WEBHOOK_URL={n8n_config['environment']['WEBHOOK_URL']} | ||
|  |       - WEBHOOK_TUNNEL_URL={n8n_config['environment']['WEBHOOK_TUNNEL_URL']} | ||
|  |       - N8N_PUSH_BACKEND={n8n_config['environment']['N8N_PUSH_BACKEND']} | ||
|  |       - GENERIC_TIMEZONE={n8n_config['environment']['GENERIC_TIMEZONE']} | ||
|  |       - N8N_ENCRYPTION_KEY={n8n_config['environment']['N8N_ENCRYPTION_KEY']} | ||
|  |       - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS={n8n_config['environment']['N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS']} | ||
|  |       - N8N_PROTOCOL={n8n_config['environment']['N8N_PROTOCOL']} | ||
|  |       - N8N_SECURE_COOKIE={n8n_config['environment']['N8N_SECURE_COOKIE']} | ||
|  |     ports: | ||
|  |       - "{self.data['port']}:5678" | ||
|  |     depends_on: | ||
|  |       - {self.data['ServiceName']}-db | ||
|  |     volumes: | ||
|  |       - "/home/docker/{self.data['finalURL']}/data:/home/node/.n8n" | ||
|  |     networks: | ||
|  |       - n8n-network | ||
|  |     deploy: | ||
|  |       resources: | ||
|  |         limits: | ||
|  |           cpus: '{self.data["CPUsSite"]}' | ||
|  |           memory: {self.data["MemorySite"]}M | ||
|  | 
 | ||
|  | networks: | ||
|  |   n8n-network: | ||
|  |     driver: bridge | ||
|  |     name: {self.data['ServiceName']}_network'''
 | ||
|  | 
 | ||
|  | def Main(): | ||
|  |     try: | ||
|  | 
 | ||
|  |         parser = argparse.ArgumentParser(description='CyberPanel Docker Sites') | ||
|  |         parser.add_argument('function', help='Specify a function to call!') | ||
|  |         parser.add_argument('--port', help='') | ||
|  |         parser.add_argument('--htaccess', help='') | ||
|  |         parser.add_argument('--externalApp', help='') | ||
|  |         parser.add_argument('--domain', help='') | ||
|  | 
 | ||
|  |         args = parser.parse_args() | ||
|  | 
 | ||
|  |         if args.function == "SetupProxy": | ||
|  |             Docker_Sites.SetupProxy(args.port) | ||
|  |         elif args.function == 'SetupHTAccess': | ||
|  |             Docker_Sites.SetupHTAccess(args.port, args.htaccess) | ||
|  |         elif args.function == 'SetupN8NVhost': | ||
|  |             Docker_Sites.SetupN8NVhost(args.domain, args.port) | ||
|  |         elif args.function == 'DeployWPDocker': | ||
|  |             # Takes | ||
|  |             # ComposePath, MySQLPath, MySQLRootPass, MySQLDBName, MySQLDBNUser, MySQLPassword, CPUsMySQL, MemoryMySQL, | ||
|  |             # port, SitePath, CPUsSite, MemorySite, SiteName | ||
|  |             # finalURL, blogTitle, adminUser, adminPassword, adminEmail, htaccessPath, externalApp | ||
|  |             data = { | ||
|  |                 "JobID": '/home/cyberpanel/hey.txt', | ||
|  |                 "ComposePath": "/home/docker.cyberpanel.net/docker-compose.yml", | ||
|  |                 "MySQLPath": '/home/docker.cyberpanel.net/public_html/sqldocker', | ||
|  |                 "MySQLRootPass": 'testdbwp12345', | ||
|  |                 "MySQLDBName": 'testdbwp', | ||
|  |                 "MySQLDBNUser": 'testdbwp', | ||
|  |                 "MySQLPassword": 'testdbwp12345', | ||
|  |                 "CPUsMySQL": '2', | ||
|  |                 "MemoryMySQL": '512', | ||
|  |                 "port": '8000', | ||
|  |                 "SitePath": '/home/docker.cyberpanel.net/public_html/wpdocker', | ||
|  |                 "CPUsSite": '2', | ||
|  |                 "MemorySite": '512', | ||
|  |                 "SiteName": 'wp docker test', | ||
|  |                 "finalURL": 'docker.cyberpanel.net', | ||
|  |                 "blogTitle": 'docker site', | ||
|  |                 "adminUser": 'testdbwp', | ||
|  |                 "adminPassword": 'testdbwp', | ||
|  |                 "adminEmail": 'usman@cyberpersons.com', | ||
|  |                 "htaccessPath": '/home/docker.cyberpanel.net/public_html/.htaccess', | ||
|  |                 "externalApp": 'docke8463', | ||
|  |                 "docRoot": "/home/docker.cyberpanel.net" | ||
|  |             } | ||
|  |             ds = Docker_Sites('', data) | ||
|  |             ds.DeployN8NContainer() | ||
|  | 
 | ||
|  |         elif args.function == 'DeleteDockerApp': | ||
|  |             data = { | ||
|  |                 "domain": args.domain} | ||
|  |             ds = Docker_Sites('', data) | ||
|  |             ds.DeleteDockerApp() | ||
|  | 
 | ||
|  | 
 | ||
|  |     except BaseException as msg: | ||
|  |         print(str(msg)) | ||
|  |         pass | ||
|  | 
 | ||
|  | 
 | ||
|  | if __name__ == "__main__": | ||
|  |     Main() |