app.controller('installDocker', function ($scope, $http, $timeout, $window) { $scope.installDockerStatus = true; $scope.installBoxGen = true; $scope.dockerInstallBTN = false; $scope.installDocker = function () { $scope.installDockerStatus = false; $scope.installBoxGen = true; $scope.dockerInstallBTN = true; url = "/docker/installDocker"; var data = {}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas); function ListInitialDatas(response) { $scope.cyberPanelLoading = true; if (response.data.status === 1) { $scope.installBoxGen = false; getRequestStatus(); } else { new PNotify({ title: 'Operation Failed!', text: response.data.error_message, type: 'error' }); } } function cantLoadInitialDatas(response) { $scope.cyberPanelLoading = true; new PNotify({ title: 'Operation Failed!', text: 'Could not connect to server, please refresh this page', type: 'error' }); } }; function getRequestStatus() { $scope.cyberPanelLoading = false; url = "/serverstatus/switchTOLSWSStatus"; var data = {}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas); function ListInitialDatas(response) { if (response.data.abort === 0) { $scope.requestData = response.data.requestStatus; $timeout(getRequestStatus, 1000); } else { // Notifications $scope.cyberPanelLoading = true; $timeout.cancel(); $scope.requestData = response.data.requestStatus; if (response.data.installed === 1) { $timeout(function () { $window.location.reload(); }, 3000); } } } function cantLoadInitialDatas(response) { $scope.cyberPanelLoading = true; new PNotify({ title: 'Operation Failed!', text: 'Could not connect to server, please refresh this page', type: 'error' }); } } }); /* Java script code for docker management */ var delayTimer = null; app.controller('dockerImages', function ($scope) { $scope.tagList = []; $scope.imageTag = {}; }); /* Java script code to install Container */ app.controller('runContainer', function ($scope, $http) { $scope.containerCreationLoading = false; $scope.installationDetailsForm = false; $scope.installationProgress = true; $scope.errorMessageBox = true; $scope.success = true; $scope.couldNotConnect = true; $scope.goBackDisable = true; $scope.volList = {}; $scope.volListNumber = 0; $scope.eport = {}; $scope.iport = {}; $scope.portType = {}; $scope.envList = {}; // Advanced Environment Variable Mode $scope.advancedEnvMode = false; // Network configuration $scope.selectedNetwork = 'bridge'; $scope.networkMode = 'bridge'; $scope.extraHosts = ''; $scope.availableNetworks = []; // Load available Docker networks $scope.loadAvailableNetworks = function() { var url = "/docker/getDockerNetworks"; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, {}, config).then(function(response) { if (response.data.status === 1) { $scope.availableNetworks = response.data.networks; } }, function(error) { console.error('Failed to load networks:', error); }); }; // Initialize networks on page load $scope.loadAvailableNetworks(); // Helper function to generate Docker Compose YAML $scope.generateDockerComposeYml = function(containerInfo) { var yml = 'version: \'3.8\'\n\n'; yml += 'services:\n'; yml += ' ' + containerInfo.name + ':\n'; yml += ' image: ' + containerInfo.image + '\n'; yml += ' container_name: ' + containerInfo.name + '\n'; // Add ports var ports = Object.keys(containerInfo.ports); if (ports.length > 0) { yml += ' ports:\n'; for (var i = 0; i < ports.length; i++) { var port = ports[i]; if (containerInfo.ports[port]) { yml += ' - "' + containerInfo.ports[port] + ':' + port + '"\n'; } } } // Add volumes var volumes = Object.keys(containerInfo.volumes); if (volumes.length > 0) { yml += ' volumes:\n'; for (var i = 0; i < volumes.length; i++) { var volume = volumes[i]; if (containerInfo.volumes[volume]) { yml += ' - ' + containerInfo.volumes[volume] + ':' + volume + '\n'; } } } // Add environment variables var envVars = Object.keys(containerInfo.environment); if (envVars.length > 0) { yml += ' environment:\n'; for (var i = 0; i < envVars.length; i++) { var envVar = envVars[i]; yml += ' - ' + envVar + '=' + containerInfo.environment[envVar] + '\n'; } } // Add restart policy yml += ' restart: unless-stopped\n'; return yml; }; $scope.advancedEnvText = ''; $scope.advancedEnvCount = 0; $scope.parsedEnvVars = {}; $scope.addVolField = function () { $scope.volList[$scope.volListNumber] = {'dest': '', 'src': ''}; $scope.volListNumber = $scope.volListNumber + 1; console.log($scope.volList) }; $scope.removeVolField = function () { delete $scope.volList[$scope.volListNumber - 1]; $scope.volListNumber = $scope.volListNumber - 1; }; $scope.addEnvField = function () { var countEnv = Object.keys($scope.envList).length; $scope.envList[countEnv + 1] = {'name': '', 'value': ''}; }; // Advanced Environment Variable Functions $scope.toggleEnvMode = function() { if ($scope.advancedEnvMode) { // Switching to advanced mode - convert existing envList to text format $scope.convertToAdvancedFormat(); } else { // Switching to simple mode - convert advanced text to envList $scope.convertToSimpleFormat(); } }; $scope.convertToAdvancedFormat = function() { var envLines = []; for (var key in $scope.envList) { if ($scope.envList[key].name && $scope.envList[key].value) { envLines.push($scope.envList[key].name + '=' + $scope.envList[key].value); } } $scope.advancedEnvText = envLines.join('\n'); $scope.parseAdvancedEnv(); }; $scope.convertToSimpleFormat = function() { $scope.parseAdvancedEnv(); var newEnvList = {}; var index = 0; for (var key in $scope.parsedEnvVars) { newEnvList[index] = {'name': key, 'value': $scope.parsedEnvVars[key]}; index++; } $scope.envList = newEnvList; }; $scope.parseAdvancedEnv = function() { $scope.parsedEnvVars = {}; $scope.advancedEnvCount = 0; if (!$scope.advancedEnvText) { return; } var lines = $scope.advancedEnvText.split('\n'); for (var i = 0; i < lines.length; i++) { var line = lines[i].trim(); // Skip empty lines and comments if (!line || line.startsWith('#')) { continue; } // Parse KEY=VALUE format var equalIndex = line.indexOf('='); if (equalIndex > 0) { var key = line.substring(0, equalIndex).trim(); var value = line.substring(equalIndex + 1).trim(); // Remove quotes if present if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) { value = value.slice(1, -1); } if (key && key.match(/^[A-Za-z_][A-Za-z0-9_]*$/)) { $scope.parsedEnvVars[key] = value; $scope.advancedEnvCount++; } } } }; $scope.loadEnvTemplate = function() { var templates = { 'web-app': 'NODE_ENV=production\nPORT=3000\nDATABASE_URL=postgresql://user:pass@localhost/db\nREDIS_URL=redis://localhost:6379\nJWT_SECRET=your-jwt-secret\nAPI_KEY=your-api-key', 'database': 'POSTGRES_DB=myapp\nPOSTGRES_USER=user\nPOSTGRES_PASSWORD=password\nPOSTGRES_HOST=localhost\nPOSTGRES_PORT=5432', 'api': 'API_HOST=0.0.0.0\nAPI_PORT=8080\nLOG_LEVEL=info\nCORS_ORIGIN=*\nRATE_LIMIT=1000\nAPI_KEY=your-secret-key', 'monitoring': 'PROMETHEUS_PORT=9090\nGRAFANA_PORT=3000\nALERTMANAGER_PORT=9093\nRETENTION_TIME=15d\nSCRAPE_INTERVAL=15s' }; var templateNames = Object.keys(templates); var templateChoice = prompt('Choose a template:\n' + templateNames.map((name, i) => (i + 1) + '. ' + name).join('\n') + '\n\nEnter number or template name:'); if (templateChoice) { var templateIndex = parseInt(templateChoice) - 1; var selectedTemplate = null; if (templateIndex >= 0 && templateIndex < templateNames.length) { selectedTemplate = templates[templateNames[templateIndex]]; } else { // Try to find by name var templateName = templateChoice.toLowerCase().replace(/\s+/g, '-'); if (templates[templateName]) { selectedTemplate = templates[templateName]; } } if (selectedTemplate) { if ($scope.advancedEnvMode) { $scope.advancedEnvText = selectedTemplate; $scope.parseAdvancedEnv(); } else { // Convert template to simple format var lines = selectedTemplate.split('\n'); $scope.envList = {}; var index = 0; for (var i = 0; i < lines.length; i++) { var line = lines[i].trim(); if (line && !line.startsWith('#')) { var equalIndex = line.indexOf('='); if (equalIndex > 0) { $scope.envList[index] = { 'name': line.substring(0, equalIndex).trim(), 'value': line.substring(equalIndex + 1).trim() }; index++; } } } } new PNotify({ title: 'Template Loaded', text: 'Environment variable template has been loaded successfully', type: 'success' }); } } }; // Docker Compose Functions for runContainer $scope.generateDockerCompose = function() { // Get container information from form var containerInfo = { name: $scope.name || 'my-container', image: $scope.image || 'nginx:latest', ports: $scope.eport || {}, volumes: $scope.volList || {}, environment: {} }; // Collect environment variables if ($scope.advancedEnvMode && $scope.parsedEnvVars) { containerInfo.environment = $scope.parsedEnvVars; } else { for (var key in $scope.envList) { if ($scope.envList[key].name && $scope.envList[key].value) { containerInfo.environment[$scope.envList[key].name] = $scope.envList[key].value; } } } // Generate docker-compose.yml content var composeContent = $scope.generateDockerComposeYml(containerInfo); // Create and download file var blob = new Blob([composeContent], { type: 'text/yaml' }); var url = window.URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = 'docker-compose.yml'; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); new PNotify({ title: 'Docker Compose Generated', text: 'docker-compose.yml file has been generated and downloaded', type: 'success' }); }; $scope.generateEnvFile = function() { var envText = ''; if ($scope.advancedEnvMode && $scope.advancedEnvText) { envText = $scope.advancedEnvText; } else { // Convert simple mode to .env format for (var key in $scope.envList) { if ($scope.envList[key].name && $scope.envList[key].value) { envText += $scope.envList[key].name + '=' + $scope.envList[key].value + '\n'; } } } if (!envText.trim()) { new PNotify({ title: 'Nothing to Generate', text: 'No environment variables to generate .env file', type: 'warning' }); return; } // Create and download file var blob = new Blob([envText], { type: 'text/plain' }); var url = window.URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = '.env'; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); new PNotify({ title: '.env File Generated', text: '.env file has been generated and downloaded', type: 'success' }); }; $scope.showComposeHelp = function() { var helpContent = `

How to use Docker Compose with Environment Variables

Step 1: Download Files

Click "Generate docker-compose.yml" and "Generate .env file" to download both files.

Step 2: Place Files

Place both files in the same directory on your server.

Step 3: Run Docker Compose

Run the following commands in your terminal:

docker compose up -d
Step 4: Update Environment Variables

To update environment variables:

  1. Edit the .env file
  2. Run: docker compose up -d
  3. Only the environment variables will be reloaded (no container rebuild needed!)
Benefits:
`; // Create modal for help var modal = document.createElement('div'); modal.className = 'modal fade'; modal.innerHTML = ` `; document.body.appendChild(modal); $(modal).modal('show'); // Remove modal when closed $(modal).on('hidden.bs.modal', function() { document.body.removeChild(modal); }); }; $scope.loadEnvFromFile = function() { var input = document.createElement('input'); input.type = 'file'; input.accept = '.env,text/plain'; input.onchange = function(event) { var file = event.target.files[0]; if (file) { var reader = new FileReader(); reader.onload = function(e) { $scope.advancedEnvText = e.target.result; $scope.parseAdvancedEnv(); $scope.$apply(); new PNotify({ title: 'File Loaded', text: 'Environment variables loaded from file successfully', type: 'success' }); }; reader.readAsText(file); } }; input.click(); }; $scope.copyEnvToClipboard = function() { var textToCopy = ''; if ($scope.advancedEnvMode) { textToCopy = $scope.advancedEnvText; } else { // Convert simple format to text var envLines = []; for (var key in $scope.envList) { if ($scope.envList[key].name && $scope.envList[key].value) { envLines.push($scope.envList[key].name + '=' + $scope.envList[key].value); } } textToCopy = envLines.join('\n'); } if (textToCopy) { navigator.clipboard.writeText(textToCopy).then(function() { new PNotify({ title: 'Copied to Clipboard', text: 'Environment variables copied to clipboard', type: 'success' }); }).catch(function(err) { // Fallback for older browsers var textArea = document.createElement('textarea'); textArea.value = textToCopy; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); new PNotify({ title: 'Copied to Clipboard', text: 'Environment variables copied to clipboard', type: 'success' }); }); } }; $scope.clearAdvancedEnv = function() { $scope.advancedEnvText = ''; $scope.parsedEnvVars = {}; $scope.advancedEnvCount = 0; }; var statusFile; // Watch for changes to validate ports $scope.$watchGroup(['name', 'dockerOwner', 'memory'], function() { $scope.updateFormValidity(); }); $scope.$watch('eport', function() { $scope.updateFormValidity(); }, true); $scope.formIsValid = false; $scope.updateFormValidity = function() { // Basic required fields if (!$scope.name || !$scope.dockerOwner || !$scope.memory) { $scope.formIsValid = false; return; } // Check if all port mappings are filled (if they exist) if ($scope.portType && Object.keys($scope.portType).length > 0) { for (var port in $scope.portType) { if (!$scope.eport || !$scope.eport[port]) { $scope.formIsValid = false; return; } } } $scope.formIsValid = true; }; $scope.createContainer = function () { $scope.containerCreationLoading = true; $scope.installationDetailsForm = true; $scope.installationProgress = false; $scope.errorMessageBox = true; $scope.success = true; $scope.couldNotConnect = true; $scope.goBackDisable = true; $scope.currentStatus = "Starting creation.."; url = "/docker/submitContainerCreation"; var name = $scope.name; var tag = $scope.tag; var memory = $scope.memory; var dockerOwner = $scope.dockerOwner; var image = $scope.image; var numberOfEnv = Object.keys($scope.envList).length; // Prepare environment variables based on mode var finalEnvList = {}; if ($scope.advancedEnvMode && $scope.parsedEnvVars) { // Use parsed environment variables from advanced mode finalEnvList = $scope.parsedEnvVars; } else { // Convert simple envList to proper format for (var key in $scope.envList) { if ($scope.envList[key].name && $scope.envList[key].value) { finalEnvList[$scope.envList[key].name] = $scope.envList[key].value; } } } var data = { name: name, tag: tag, memory: memory, dockerOwner: dockerOwner, image: image, envList: finalEnvList, volList: $scope.volList, advancedEnvMode: $scope.advancedEnvMode, network: $scope.selectedNetwork, network_mode: $scope.networkMode, extraOptions: { add_host: $scope.extraHosts } }; try { $.each($scope.portType, function (port, protocol) { data[port + "/" + protocol] = $scope.eport[port]; }); } catch (err) { } var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas); function ListInitialDatas(response) { if (response.data.createContainerStatus === 1) { $scope.currentStatus = "Successful. Redirecting..."; window.location.href = "/docker/view/" + $scope.name } else { $scope.containerCreationLoading = true; $scope.installationDetailsForm = true; $scope.installationProgress = false; $scope.errorMessageBox = false; $scope.success = true; $scope.couldNotConnect = true; $scope.goBackDisable = false; $scope.errorMessage = response.data.error_message; } } function cantLoadInitialDatas(response) { $scope.containerCreationLoading = true; $scope.installationDetailsForm = true; $scope.installationProgress = false; $scope.errorMessageBox = true; $scope.success = true; $scope.couldNotConnect = false; $scope.goBackDisable = false; } }; $scope.goBack = function () { $scope.containerCreationLoading = true; $scope.installationDetailsForm = false; $scope.installationProgress = true; $scope.errorMessageBox = true; $scope.success = true; $scope.couldNotConnect = true; $scope.goBackDisable = true; $("#installProgress").css("width", "0%"); }; }); /* Javascript code for listing containers */ app.controller('listContainers', function ($scope, $http) { $scope.activeLog = ""; $scope.assignActive = ""; $scope.dockerOwner = ""; $scope.assignContainer = function (name) { console.log('assignContainer called with:', name); $scope.assignActive = name; console.log('assignActive set to:', $scope.assignActive); $("#assign").modal("show"); }; // Test function to verify scope is working $scope.testScope = function() { alert('Scope is working! assignActive: ' + $scope.assignActive + ', dockerOwner: ' + $scope.dockerOwner); }; $scope.submitAssignContainer = function () { console.log('submitAssignContainer called'); console.log('dockerOwner:', $scope.dockerOwner); console.log('assignActive:', $scope.assignActive); if (!$scope.dockerOwner) { new PNotify({ title: 'Error', text: 'Please select an owner', type: 'error' }); return; } if (!$scope.assignActive) { new PNotify({ title: 'Error', text: 'No container selected', type: 'error' }); return; } url = "/docker/assignContainer"; var data = {name: $scope.assignActive, admin: $scope.dockerOwner}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { if (response.data.assignContainerStatus === 1) { new PNotify({ title: 'Container assigned successfully', type: 'success' }); window.location.href = '/docker/listContainers'; } else { new PNotify({ title: 'Unable to complete request', text: response.data.error_message, type: 'error' }); } $("#assign").modal("hide"); } function cantLoadInitialData(response) { console.log("not good"); new PNotify({ title: 'Unable to complete request', type: 'error' }); $("#assign").modal("hide"); } }; $scope.delContainer = function (name, unlisted=false) { (new PNotify({ title: 'Confirmation Needed', text: 'Are you sure?', icon: 'fa fa-question-circle', hide: false, confirm: { confirm: true }, buttons: { closer: false, sticker: false }, history: { history: false } })).get().on('pnotify.confirm', function () { $('#imageLoading').show(); url = "/docker/delContainer"; var data = {name: name, unlisted: unlisted}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { console.log(response); if (response.data.delContainerStatus === 1) { location.reload(); } else if (response.data.delContainerStatus === 2) { (new PNotify({ title: response.data.error_message, text: 'Delete anyway?', icon: 'fa fa-question-circle', hide: false, confirm: { confirm: true }, buttons: { closer: false, sticker: false }, history: { history: false } })).get().on('pnotify.confirm', function () { url = "/docker/delContainer"; var data = {name: name, unlisted: unlisted, force: 1}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { if (response.data.delContainerStatus === 1) { location.reload(); } else { $("#listFail").fadeIn(); $scope.errorMessage = response.data.error_message; } $('#imageLoading').hide(); } function cantLoadInitialData(response) { $('#imageLoading').hide(); } }) } else { $("#listFail").fadeIn(); $scope.errorMessage = response.data.error_message; } $('#imageLoading').hide(); } function cantLoadInitialData(response) { $('#imageLoading').hide(); } }) } // Update Container Functions $scope.showUpdateModal = function (name, currentImage, currentTag) { $scope.updateContainerName = name; $scope.currentImage = currentImage; $scope.currentTag = currentTag; $scope.newImage = ''; $scope.newTag = 'latest'; $("#updateContainer").modal("show"); }; $scope.performUpdate = function () { if (!$scope.updateContainerName) { new PNotify({ title: 'Error', text: 'No container selected', type: 'error' }); return; } // If no new image specified, use current image if (!$scope.newImage) { $scope.newImage = $scope.currentImage; } // If no new tag specified, use latest if (!$scope.newTag) { $scope.newTag = 'latest'; } (new PNotify({ title: 'Update Confirmation', text: `Are you sure you want to update container "${$scope.updateContainerName}" to ${$scope.newImage}:${$scope.newTag}? This will preserve all data.`, icon: 'fa fa-question-circle', hide: false, confirm: { confirm: true }, buttons: { closer: false, sticker: false }, history: { history: false } })).get().on('pnotify.confirm', function () { $('#imageLoading').show(); $("#updateContainer").modal("hide"); url = "/docker/updateContainer"; var data = { name: $scope.updateContainerName, newImage: $scope.newImage, newTag: $scope.newTag }; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { console.log(response); $('#imageLoading').hide(); if (response.data.updateContainerStatus === 1) { new PNotify({ title: 'Container Updated Successfully', text: `Container updated to ${response.data.new_image}`, type: 'success' }); location.reload(); } else { new PNotify({ title: 'Update Failed', text: response.data.error_message, type: 'error' }); } } function cantLoadInitialData(response) { $('#imageLoading').hide(); new PNotify({ title: 'Update Failed', text: 'Could not connect to server', type: 'error' }); } }); }; // Delete Container with Data $scope.deleteContainerWithData = function (name, unlisted = false) { (new PNotify({ title: 'Dangerous Operation', text: `Are you sure you want to delete container "${name}" and ALL its data? This action cannot be undone!`, icon: 'fa fa-exclamation-triangle', hide: false, confirm: { confirm: true }, buttons: { closer: false, sticker: false }, history: { history: false } })).get().on('pnotify.confirm', function () { $('#imageLoading').show(); url = "/docker/deleteContainerWithData"; var data = {name: name, unlisted: unlisted}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { console.log(response); $('#imageLoading').hide(); if (response.data.deleteContainerWithDataStatus === 1) { new PNotify({ title: 'Container and Data Deleted', text: 'Container and all associated data have been permanently deleted', type: 'success' }); location.reload(); } else { new PNotify({ title: 'Deletion Failed', text: response.data.error_message, type: 'error' }); } } function cantLoadInitialData(response) { $('#imageLoading').hide(); new PNotify({ title: 'Deletion Failed', text: 'Could not connect to server', type: 'error' }); } }); }; // Delete Container Keep Data $scope.deleteContainerKeepData = function (name, unlisted = false) { (new PNotify({ title: 'Delete Container (Keep Data)', text: `Are you sure you want to delete container "${name}" but keep all data? The data will be preserved in Docker volumes.`, icon: 'fa fa-save', hide: false, confirm: { confirm: true }, buttons: { closer: false, sticker: false }, history: { history: false } })).get().on('pnotify.confirm', function () { $('#imageLoading').show(); url = "/docker/deleteContainerKeepData"; var data = {name: name, unlisted: unlisted}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { console.log(response); $('#imageLoading').hide(); if (response.data.deleteContainerKeepDataStatus === 1) { var message = 'Container deleted successfully, data preserved'; if (response.data.preserved_volumes && response.data.preserved_volumes.length > 0) { message += '\nPreserved volumes: ' + response.data.preserved_volumes.join(', '); } new PNotify({ title: 'Container Deleted (Data Preserved)', text: message, type: 'success' }); location.reload(); } else { new PNotify({ title: 'Deletion Failed', text: response.data.error_message, type: 'error' }); } } function cantLoadInitialData(response) { $('#imageLoading').hide(); new PNotify({ title: 'Deletion Failed', text: 'Could not connect to server', type: 'error' }); } }); }; $scope.showLog = function (name, refresh = false) { $scope.logs = ""; $scope.logInfo = null; $scope.formattedLogs = ""; if (refresh === false) { $('#logs').modal('show'); $scope.activeLog = name; } else { name = $scope.activeLog; } $scope.logs = "Loading..."; url = "/docker/getContainerLogs"; var data = {name: name}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { console.log(response); if (response.data.containerLogStatus === 1) { $scope.logs = response.data.containerLog; $scope.logInfo = { container_status: response.data.container_status, log_count: response.data.log_count }; // Format logs for better display $scope.formatLogs(); // Auto-scroll to bottom setTimeout(function() { $scope.scrollToBottom(); }, 100); } else { $scope.logs = response.data.error_message; $scope.logInfo = null; $scope.formattedLogs = ""; new PNotify({ title: 'Unable to complete request', text: response.data.error_message, type: 'error' }); } } function cantLoadInitialData(response) { $scope.logs = "Error loading logs"; $scope.logInfo = null; $scope.formattedLogs = ""; new PNotify({ title: 'Unable to complete request', type: 'error' }); } }; // Format logs with syntax highlighting and better readability $scope.formatLogs = function() { if (!$scope.logs || $scope.logs === 'Loading...') { $scope.formattedLogs = $scope.logs; return; } var lines = $scope.logs.split('\n'); var formattedLines = []; for (var i = 0; i < lines.length; i++) { var line = lines[i]; var formattedLine = line; // Escape HTML characters formattedLine = formattedLine.replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); // Add syntax highlighting for common log patterns if (line.match(/^\[.*?\]/)) { // Timestamp lines formattedLine = '' + formattedLine + ''; } else if (line.match(/ERROR|FATAL|CRITICAL/i)) { // Error lines formattedLine = '' + formattedLine + ''; } else if (line.match(/WARN|WARNING/i)) { // Warning lines formattedLine = '' + formattedLine + ''; } else if (line.match(/INFO/i)) { // Info lines formattedLine = '' + formattedLine + ''; } else if (line.match(/DEBUG/i)) { // Debug lines formattedLine = '' + formattedLine + ''; } else if (line.match(/SUCCESS|OK|COMPLETED/i)) { // Success lines formattedLine = '' + formattedLine + ''; } formattedLines.push(formattedLine); } $scope.formattedLogs = formattedLines.join('\n'); }; // Scroll functions $scope.scrollToTop = function() { var container = document.getElementById('logContainer'); if (container) { container.scrollTop = 0; } }; $scope.scrollToBottom = function() { var container = document.getElementById('logContainer'); if (container) { container.scrollTop = container.scrollHeight; } }; // Clear logs function $scope.clearLogs = function() { $scope.logs = ""; $scope.formattedLogs = ""; $scope.logInfo = null; }; url = "/docker/getContainerList"; var data = {page: 1}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { console.log(response); if (response.data.listContainerStatus === 1) { var finalData = JSON.parse(response.data.data); $scope.ContainerList = finalData; console.log($scope.ContainerList); $("#listFail").hide(); } else { $("#listFail").fadeIn(); $scope.errorMessage = response.data.error_message; } } function cantLoadInitialData(response) { console.log("not good"); } $scope.getFurtherContainersFromDB = function (pageNumber) { var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; var data = {page: pageNumber}; dataurl = "/docker/getContainerList"; $http.post(dataurl, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { if (response.data.listContainerStatus === 1) { var finalData = JSON.parse(response.data.data); $scope.ContainerList = finalData; $("#listFail").hide(); } else { $("#listFail").fadeIn(); $scope.errorMessage = response.data.error_message; console.log(response.data); } } function cantLoadInitialData(response) { console.log("not good"); } }; }); /* Java script code for containerr home page */ app.controller('viewContainer', function ($scope, $http, $interval, $timeout) { $scope.cName = ""; $scope.status = ""; $scope.savingSettings = false; $scope.loadingTop = false; $scope.statusInterval = null; $scope.statsInterval = null; // Advanced Environment Variable Functions for viewContainer $scope.advancedEnvMode = false; $scope.advancedEnvText = ''; $scope.advancedEnvCount = 0; $scope.parsedEnvVars = {}; // Auto-refresh status every 5 seconds $scope.startStatusMonitoring = function() { $scope.statusInterval = $interval(function() { $scope.refreshStatus(true); // Silent refresh }, 5000); }; // Stop monitoring on scope destroy $scope.$on('$destroy', function() { if ($scope.statusInterval) { $interval.cancel($scope.statusInterval); } if ($scope.statsInterval) { $interval.cancel($scope.statsInterval); } }); // Start monitoring when controller loads $timeout(function() { $scope.startStatusMonitoring(); }, 1000); $scope.recreate = function () { (new PNotify({ title: 'Confirmation Needed', text: 'Are you sure?', icon: 'fa fa-question-circle', hide: false, confirm: { confirm: true }, buttons: { closer: false, sticker: false }, history: { history: false } })).get().on('pnotify.confirm', function () { $('#infoLoading').show(); url = "/docker/recreateContainer"; var data = {name: $scope.cName}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { if (response.data.recreateContainerStatus === 1) { new PNotify({ title: 'Action completed!', text: 'Redirecting...', type: 'success' }); location.reload(); } else { new PNotify({ title: 'Unable to complete request', text: response.data.error_message, type: 'error' }); } $('#infoLoading').hide(); } function cantLoadInitialData(response) { PNotify.error({ title: 'Unable to complete request', text: "Problem in connecting to server" }); $('#actionLoading').hide(); } }) }; $scope.addEnvField = function () { var countEnv = Object.keys($scope.envList).length; $scope.envList[countEnv + 1] = {'name': '', 'value': ''}; }; // Advanced Environment Variable Functions for viewContainer $scope.toggleEnvMode = function() { if ($scope.advancedEnvMode) { // Switching to advanced mode - convert existing envList to text format $scope.convertToAdvancedFormat(); } else { // Switching to simple mode - convert advanced text to envList $scope.convertToSimpleFormat(); } }; $scope.convertToAdvancedFormat = function() { var envLines = []; for (var key in $scope.envList) { if ($scope.envList[key].name && $scope.envList[key].value) { envLines.push($scope.envList[key].name + '=' + $scope.envList[key].value); } } $scope.advancedEnvText = envLines.join('\n'); $scope.parseAdvancedEnv(); }; $scope.convertToSimpleFormat = function() { $scope.parseAdvancedEnv(); var newEnvList = {}; var index = 0; for (var key in $scope.parsedEnvVars) { newEnvList[index] = {'name': key, 'value': $scope.parsedEnvVars[key]}; index++; } $scope.envList = newEnvList; }; $scope.parseAdvancedEnv = function() { $scope.parsedEnvVars = {}; $scope.advancedEnvCount = 0; if (!$scope.advancedEnvText) { return; } var lines = $scope.advancedEnvText.split('\n'); for (var i = 0; i < lines.length; i++) { var line = lines[i].trim(); // Skip empty lines and comments if (!line || line.startsWith('#')) { continue; } // Parse KEY=VALUE format var equalIndex = line.indexOf('='); if (equalIndex > 0) { var key = line.substring(0, equalIndex).trim(); var value = line.substring(equalIndex + 1).trim(); // Remove quotes if present if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) { value = value.slice(1, -1); } if (key && key.match(/^[A-Za-z_][A-Za-z0-9_]*$/)) { $scope.parsedEnvVars[key] = value; $scope.advancedEnvCount++; } } } }; $scope.copyEnvToClipboard = function() { var textToCopy = ''; if ($scope.advancedEnvMode) { textToCopy = $scope.advancedEnvText; } else { // Convert simple format to text var envLines = []; for (var key in $scope.envList) { if ($scope.envList[key].name && $scope.envList[key].value) { envLines.push($scope.envList[key].name + '=' + $scope.envList[key].value); } } textToCopy = envLines.join('\n'); } if (textToCopy) { navigator.clipboard.writeText(textToCopy).then(function() { new PNotify({ title: 'Copied to Clipboard', text: 'Environment variables copied to clipboard', type: 'success' }); }).catch(function(err) { // Fallback for older browsers var textArea = document.createElement('textarea'); textArea.value = textToCopy; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); new PNotify({ title: 'Copied to Clipboard', text: 'Environment variables copied to clipboard', type: 'success' }); }); } }; // Import/Export Functions $scope.importEnvFromContainer = function() { // Show modal to select container to import from $scope.showContainerImportModal = true; $scope.loadContainersForImport(); }; $scope.loadContainersForImport = function() { $scope.importLoading = true; $scope.importContainers = []; $http.get('/dockerManager/loadContainersForImport/', { params: { currentContainer: $scope.cName } }).then(function(response) { $scope.importContainers = response.data.containers || []; $scope.importLoading = false; }).catch(function(error) { new PNotify({ title: 'Import Failed', text: 'Failed to load containers for import', type: 'error' }); $scope.importLoading = false; }); }; $scope.selectContainerForImport = function(container) { $scope.selectedImportContainer = container; $scope.loadEnvFromContainer(container.name); }; $scope.loadEnvFromContainer = function(containerName) { $scope.importEnvLoading = true; $http.get('/dockerManager/getContainerEnv/', { params: { containerName: containerName } }).then(function(response) { if (response.data.success) { var envVars = response.data.envVars || {}; if ($scope.advancedEnvMode) { // Convert to .env format var envText = ''; for (var key in envVars) { envText += key + '=' + envVars[key] + '\n'; } $scope.advancedEnvText = envText; $scope.parseAdvancedEnv(); } else { // Convert to simple mode $scope.envList = {}; var index = 0; for (var key in envVars) { $scope.envList[index] = {'name': key, 'value': envVars[key]}; index++; } } $scope.showContainerImportModal = false; new PNotify({ title: 'Import Successful', text: 'Environment variables imported from ' + containerName, type: 'success' }); } else { new PNotify({ title: 'Import Failed', text: response.data.message || 'Failed to import environment variables', type: 'error' }); } $scope.importEnvLoading = false; }).catch(function(error) { new PNotify({ title: 'Import Failed', text: 'Failed to import environment variables', type: 'error' }); $scope.importEnvLoading = false; }); }; $scope.exportEnvToFile = function() { var envText = ''; if ($scope.advancedEnvMode && $scope.advancedEnvText) { envText = $scope.advancedEnvText; } else { // Convert simple mode to .env format for (var key in $scope.envList) { if ($scope.envList[key].name && $scope.envList[key].value) { envText += $scope.envList[key].name + '=' + $scope.envList[key].value + '\n'; } } } if (!envText.trim()) { new PNotify({ title: 'Nothing to Export', text: 'No environment variables to export', type: 'warning' }); return; } // Create and download file var blob = new Blob([envText], { type: 'text/plain' }); var url = window.URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = $scope.cName + '_environment.env'; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); new PNotify({ title: 'Export Successful', text: 'Environment variables exported to file', type: 'success' }); }; // Docker Compose Functions $scope.generateDockerCompose = function() { // Get container information var containerInfo = { name: $scope.cName, image: $scope.image || 'nginx:latest', ports: $scope.ports || {}, volumes: $scope.volList || {}, environment: {} }; // Collect environment variables if ($scope.advancedEnvMode && $scope.parsedEnvVars) { containerInfo.environment = $scope.parsedEnvVars; } else { for (var key in $scope.envList) { if ($scope.envList[key].name && $scope.envList[key].value) { containerInfo.environment[$scope.envList[key].name] = $scope.envList[key].value; } } } // Generate docker-compose.yml content var composeContent = $scope.generateDockerComposeYml(containerInfo); // Create and download file var blob = new Blob([composeContent], { type: 'text/yaml' }); var url = window.URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = 'docker-compose.yml'; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); new PNotify({ title: 'Docker Compose Generated', text: 'docker-compose.yml file has been generated and downloaded', type: 'success' }); }; $scope.generateEnvFile = function() { var envText = ''; if ($scope.advancedEnvMode && $scope.advancedEnvText) { envText = $scope.advancedEnvText; } else { // Convert simple mode to .env format for (var key in $scope.envList) { if ($scope.envList[key].name && $scope.envList[key].value) { envText += $scope.envList[key].name + '=' + $scope.envList[key].value + '\n'; } } } if (!envText.trim()) { new PNotify({ title: 'Nothing to Generate', text: 'No environment variables to generate .env file', type: 'warning' }); return; } // Create and download file var blob = new Blob([envText], { type: 'text/plain' }); var url = window.URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = '.env'; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); new PNotify({ title: '.env File Generated', text: '.env file has been generated and downloaded', type: 'success' }); }; $scope.showComposeHelp = function() { var helpContent = `

How to use Docker Compose with Environment Variables

Step 1: Download Files

Click "Generate docker-compose.yml" and "Generate .env file" to download both files.

Step 2: Place Files

Place both files in the same directory on your server.

Step 3: Run Docker Compose

Run the following commands in your terminal:

docker compose up -d
Step 4: Update Environment Variables

To update environment variables:

  1. Edit the .env file
  2. Run: docker compose up -d
  3. Only the environment variables will be reloaded (no container rebuild needed!)
Benefits:
`; // Create modal for help var modal = document.createElement('div'); modal.className = 'modal fade'; modal.innerHTML = ` `; document.body.appendChild(modal); $(modal).modal('show'); // Remove modal when closed $(modal).on('hidden.bs.modal', function() { document.body.removeChild(modal); }); }; $scope.showTop = function () { $scope.topHead = []; $scope.topProcesses = []; $scope.loadingTop = true; $("#processes").modal("show"); url = "/docker/getContainerTop"; var data = {name: $scope.cName}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { if (response.data.containerTopStatus === 1) { $scope.topHead = response.data.processes.Titles; $scope.topProcesses = response.data.processes.Processes; } else { new PNotify({ title: 'Unable to complete request', text: response.data.error_message, type: 'error' }); } $scope.loadingTop = false; } function cantLoadInitialData(response) { PNotify.error({ title: 'Unable to complete request', text: "Problem in connecting to server" }); $scope.loadingTop = false; } }; $scope.cRemove = function () { (new PNotify({ title: 'Confirmation Needed', text: 'Are you sure?', icon: 'fa fa-question-circle', hide: false, confirm: { confirm: true }, buttons: { closer: false, sticker: false }, history: { history: false } })).get().on('pnotify.confirm', function () { $('#actionLoading').show(); url = "/docker/delContainer"; var data = {name: $scope.cName, unlisted: false}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { if (response.data.delContainerStatus === 1) { new PNotify({ title: 'Container deleted!', text: 'Redirecting...', type: 'success' }); window.location.href = '/docker/listContainers'; } else { new PNotify({ title: 'Unable to complete request', text: response.data.error_message, type: 'error' }); } $('#actionLoading').hide(); } function cantLoadInitialData(response) { PNotify.error({ title: 'Unable to complete request', text: "Problem in connecting to server" }); $('#actionLoading').hide(); } }) }; $scope.refreshStatus = function (silent) { url = "/docker/getContainerStatus"; var data = {name: $scope.cName}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { if (response.data.containerStatus === 1) { var oldStatus = $scope.status; $scope.status = response.data.status; // Animate status change if (oldStatus !== $scope.status && !silent) { // Add animation class var statusBadge = document.querySelector('.status-badge'); if (statusBadge) { statusBadge.classList.add('status-changed'); setTimeout(function() { statusBadge.classList.remove('status-changed'); }, 600); } new PNotify({ title: 'Status Updated', text: 'Container is now ' + $scope.status, type: 'info', delay: 2000 }); } } else { if (!silent) { new PNotify({ title: 'Unable to complete request', text: response.data.error_message, type: 'error' }); } } } function cantLoadInitialData(response) { if (!silent) { PNotify.error({ title: 'Unable to complete request', text: "Problem in connecting to server" }); } } }; $scope.addVolField = function () { $scope.volList[$scope.volListNumber] = {'dest': '', 'src': ''}; $scope.volListNumber = $scope.volListNumber + 1; }; $scope.removeVolField = function () { delete $scope.volList[$scope.volListNumber - 1]; $scope.volListNumber = $scope.volListNumber - 1; }; $scope.saveSettings = function () { $('#containerSettingLoading').show(); url = "/docker/saveContainerSettings"; $scope.savingSettings = true; // Prepare environment variables based on mode var finalEnvList = {}; if ($scope.advancedEnvMode && $scope.parsedEnvVars) { // Use parsed environment variables from advanced mode finalEnvList = $scope.parsedEnvVars; } else { // Convert simple envList to proper format for (var key in $scope.envList) { if ($scope.envList[key].name && $scope.envList[key].value) { finalEnvList[$scope.envList[key].name] = $scope.envList[key].value; } } } var data = { name: $scope.cName, memory: $scope.memory, startOnReboot: $scope.startOnReboot, envConfirmation: $scope.envConfirmation, envList: finalEnvList, volList: $scope.volList, advancedEnvMode: $scope.advancedEnvMode }; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { if (response.data.saveSettingsStatus === 1) { if ($scope.envConfirmation) { new PNotify({ title: 'Done. Redirecting...', type: 'success' }); location.reload(); } else { new PNotify({ title: 'Settings Saved', type: 'success' }); } } else { new PNotify({ title: 'Unable to complete request', text: response.data.error_message, type: 'error' }); } $('#containerSettingLoading').hide(); $scope.savingSettings = false; } function cantLoadInitialData(response) { new PNotify({ title: 'Unable to complete request', text: "Problem in connecting to server", type: 'error' }); $('#containerSettingLoading').hide(); $scope.savingSettings = false; } if ($scope.startOnReboot === true) { $scope.rPolicy = "Yes"; } else { $scope.rPolicy = "No"; } }; $scope.cAction = function (action) { $('#actionLoading').show(); url = "/docker/doContainerAction"; var data = {name: $scope.cName, action: action}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { console.log(response); if (response.data.containerActionStatus === 1) { new PNotify({ title: 'Success!', text: 'Action completed', type: 'success' }); $scope.status = response.data.status; $scope.refreshStatus() } else { new PNotify({ title: 'Unable to complete request', text: response.data.error_message, type: 'error' }); } $('#actionLoading').hide(); } function cantLoadInitialData(response) { PNotify.error({ title: 'Unable to complete request', text: "Problem in connecting to server" }); $('#actionLoading').hide(); } }; $scope.loadLogs = function (name) { $scope.logs = "Loading..."; url = "/docker/getContainerLogs"; var data = {name: name}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { console.log(response); if (response.data.containerLogStatus === 1) { $scope.logs = response.data.containerLog; } else { $scope.logs = response.data.error_message; } } function cantLoadInitialData(response) { console.log("not good"); $scope.logs = "Error loading log"; } }; // Command execution functionality $scope.commandToExecute = ''; $scope.executingCommand = false; $scope.commandOutput = null; $scope.commandHistory = []; $scope.showCommandModal = function() { $scope.commandToExecute = ''; $scope.commandOutput = null; $("#commandModal").modal("show"); }; // Port editing functionality $scope.showPortEditModal = function() { // Initialize current ports from container data $scope.currentPorts = {}; if ($scope.ports) { for (var iport in $scope.ports) { var eport = $scope.ports[iport]; if (eport && eport.length > 0) { $scope.currentPorts[iport] = eport[0].HostPort; } } } $("#portEditModal").modal("show"); }; $scope.addNewPortMapping = function() { var containerPort = prompt('Enter container port (e.g., 80/tcp):'); if (containerPort) { $scope.currentPorts[containerPort] = ''; $scope.$apply(); } }; $scope.removePortMapping = function(containerPort) { if (confirm('Are you sure you want to remove this port mapping?')) { delete $scope.currentPorts[containerPort]; } }; $scope.updatePortMappings = function() { $("#portEditLoading").show(); $scope.updatingPorts = true; var url = "/docker/updateContainerPorts"; var data = { name: $scope.cName, ports: $scope.currentPorts }; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(function(response) { $("#portEditLoading").hide(); $scope.updatingPorts = false; if (response.data.status === 1) { $("#portEditModal").modal("hide"); // Refresh container status and ports $scope.refreshContainerInfo(); new PNotify({ title: 'Success', text: 'Port mappings updated successfully', type: 'success' }); } else { new PNotify({ title: 'Error', text: 'Failed to update port mappings: ' + response.data.error_message, type: 'error' }); } }, function(error) { $("#portEditLoading").hide(); $scope.updatingPorts = false; new PNotify({ title: 'Error', text: 'Error updating port mappings: ' + error.data.error_message, type: 'error' }); }); }; $scope.executeCommand = function() { if (!$scope.commandToExecute.trim()) { new PNotify({ title: 'Error', text: 'Please enter a command to execute', type: 'error' }); return; } $scope.executingCommand = true; $scope.commandOutput = null; url = "/docker/executeContainerCommand"; var data = { name: $scope.cName, command: $scope.commandToExecute.trim() }; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { console.log(response); $scope.executingCommand = false; if (response.data.commandStatus === 1) { $scope.commandOutput = { command: response.data.command, output: response.data.output, exit_code: response.data.exit_code, container_was_started: response.data.container_was_started }; // Add to command history $scope.commandHistory.unshift({ command: response.data.command, timestamp: new Date(), container_was_started: response.data.container_was_started }); // Keep only last 10 commands if ($scope.commandHistory.length > 10) { $scope.commandHistory = $scope.commandHistory.slice(0, 10); } // Show success notification with container status info var notificationText = 'Command completed with exit code: ' + response.data.exit_code; if (response.data.container_was_started) { notificationText += ' (Container was temporarily started and stopped)'; } new PNotify({ title: 'Command Executed', text: notificationText, type: response.data.exit_code === 0 ? 'success' : 'warning' }); } else { new PNotify({ title: 'Command Execution Failed', text: response.data.error_message, type: 'error' }); } } function cantLoadInitialData(response) { $scope.executingCommand = false; new PNotify({ title: 'Command Execution Failed', text: 'Could not connect to server', type: 'error' }); } }; $scope.selectCommand = function(command) { $scope.commandToExecute = command; }; $scope.clearOutput = function() { $scope.commandOutput = null; }; }); /* Java script code for docker image management */ app.controller('manageImages', function ($scope, $http) { $scope.tagList = []; $scope.showingSearch = false; $("#searchResult").hide(); $scope.pullImage = function (image, tag) { function ListInitialDatas(response) { if (response.data.installImageStatus === 1) { new PNotify({ title: 'Image pulled successfully', text: 'Reloading...', type: 'success' }); location.reload() } else { new PNotify({ title: 'Failed to complete request', text: response.data.error_message, type: 'error' }); } $('#imageLoading').hide(); } function cantLoadInitialDatas(response) { $('#imageLoading').hide(); new PNotify({ title: 'Failed to complete request', type: 'error' }); } if (image && tag) { $('#imageLoading').show(); url = "/docker/installImage"; var data = { image: image, tag: tag }; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas); } else { new PNotify({ title: 'Unable to complete request', text: 'Please select a tag', type: 'info' }); } } $scope.searchImages = function () { console.log($scope.searchString); if (!$scope.searchString) { $("#searchResult").hide(); } else { $("#searchResult").show(); } clearTimeout(delayTimer); delayTimer = setTimeout(function () { $('#imageLoading').show(); url = "/docker/searchImage"; var data = { string: $scope.searchString }; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas); function ListInitialDatas(response) { if (response.data.searchImageStatus === 1) { $scope.images = response.data.matches; console.log($scope.images) } else { new PNotify({ title: 'Failed to complete request', text: response.data.error, type: 'error' }); } $('#imageLoading').hide(); } function cantLoadInitialDatas(response) { $('#imageLoading').hide(); new PNotify({ title: 'Failed to complete request', type: 'error' }); } }, 500); } function populateTagList(image, page) { $('imageLoading').show(); url = "/docker/getTags" var data = { image: image, page: page + 1 }; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { if (response.data.getTagsStatus === 1) { $scope.tagList[image].splice(-1, 1); $scope.tagList[image] = $scope.tagList[image].concat(response.data.list); if (response.data.next != null) { $scope.tagList[image].push("Load more"); } } else { new PNotify({ title: 'Unable to complete request', text: response.data.error_message, type: 'error' }); } $('#imageLoading').hide(); } function cantLoadInitialData(response) { new PNotify({ title: 'Unable to complete request', text: response.data.error_message, type: 'error' }); $('#imageLoading').hide(); } } $scope.runContainer = function (image) { $("#errorMessage").hide(); if ($scope.imageTag[image] !== undefined) { $("#imageList").css("pointer-events", "none"); } else { $("#errorMessage").show(); $scope.errorMessage = "Please select a tag"; } } $scope.loadTags = function (event) { var pagesloaded = $(event.target).data('pageloaded'); var image = event.target.id; if (!pagesloaded) { $scope.tagList[image] = ['Loading...']; $(event.target).data('pageloaded', 1); populateTagList(image, pagesloaded); // $("#"+image+" option:selected").prop("selected", false); } } $scope.selectTag = function () { var image = event.target.id; var selectedTag = $('#' + image).find(":selected").text(); if (selectedTag == 'Load more') { var pagesloaded = $(event.target).data('pageloaded'); $(event.target).data('pageloaded', pagesloaded + 1); populateTagList(image, pagesloaded); } } $scope.getHistory = function (counter) { $('#imageLoading').show(); var name = $("#" + counter).val() url = "/docker/getImageHistory"; var data = {name: name}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { console.log(response); if (response.data.imageHistoryStatus === 1) { $('#history').modal('show'); $scope.historyList = response.data.history; } else { new PNotify({ title: 'Unable to complete request', text: response.data.error_message, type: 'error' }); } $('#imageLoading').hide(); } function cantLoadInitialData(response) { new PNotify({ title: 'Unable to complete request', text: response.data.error_message, type: 'error' }); $('#imageLoading').hide(); } } $scope.rmImage = function (counter) { (new PNotify({ title: 'Confirmation Needed', text: 'Are you sure you want to remove this image?', icon: 'fa fa-question-circle', hide: false, confirm: { confirm: true }, buttons: { closer: false, sticker: false }, history: { history: false } })).get().on('pnotify.confirm', function () { $('#imageLoading').show(); if (counter == '0') { var name = 0; var force = false; } else { var name = $("#" + counter).val() var force = false; } url = "/docker/removeImage"; var data = {name: name, force: force}; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(ListInitialData, cantLoadInitialData); function ListInitialData(response) { console.log(response); if (response.data.removeImageStatus === 1) { new PNotify({ title: 'Image(s) removed', text: 'Image has been successfully removed', type: 'success' }); window.location.href = "/docker/manageImages"; } else { var errorMessage = response.data.error_message; // Check if it's a conflict error and offer force removal if (errorMessage && errorMessage.includes("still being used by containers")) { new PNotify({ title: 'Image in Use', text: errorMessage + ' Would you like to force remove it?', icon: 'fa fa-exclamation-triangle', hide: false, confirm: { confirm: true }, buttons: { closer: false, sticker: false }, history: { history: false } }).get().on('pnotify.confirm', function () { // Force remove the image $('#imageLoading').show(); var forceData = {name: name, force: true}; $http.post(url, forceData, config).then(function(forceResponse) { $('#imageLoading').hide(); if (forceResponse.data.removeImageStatus === 1) { new PNotify({ title: 'Image Force Removed', text: 'Image has been force removed successfully', type: 'success' }); window.location.href = "/docker/manageImages"; } else { new PNotify({ title: 'Force Removal Failed', text: forceResponse.data.error_message, type: 'error' }); } }, function(forceError) { $('#imageLoading').hide(); new PNotify({ title: 'Force Removal Failed', text: 'Could not force remove the image', type: 'error' }); }); }); } else { new PNotify({ title: 'Unable to complete request', text: errorMessage, type: 'error' }); } } $('#imageLoading').hide(); } function cantLoadInitialData(response) { new PNotify({ title: 'Unable to complete request', text: response.data.error_message, type: 'error' }); $('#imageLoading').hide(); } }) } }); // Container List Controller app.controller('listContainers', function ($scope, $http, $timeout, $window) { $scope.containers = []; $scope.loading = false; $scope.updateContainerName = ''; $scope.currentImage = ''; $scope.newImage = ''; $scope.newTag = 'latest'; // Load containers list $scope.loadContainers = function() { $scope.loading = true; var url = '/docker/listContainers'; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, {}, config).then(function(response) { $scope.loading = false; if (response.data.status === 1) { $scope.containers = response.data.containers || []; } else { new PNotify({ title: 'Error Loading Containers', text: response.data.error_message || 'Failed to load containers', type: 'error' }); } }, function(error) { $scope.loading = false; new PNotify({ title: 'Connection Error', text: 'Could not connect to server', type: 'error' }); }); }; // Initialize containers on page load $scope.loadContainers(); // Open update container modal $scope.openUpdateModal = function(container) { $scope.updateContainerName = container.name; $scope.currentImage = container.image; $scope.newImage = ''; $scope.newTag = 'latest'; $('#updateContainer').modal('show'); }; // Perform container update $scope.performUpdate = function() { if (!$scope.newImage && !$scope.newTag) { new PNotify({ title: 'Missing Information', text: 'Please enter a new image name or tag', type: 'error' }); return; } // If no new image specified, use current image with new tag var imageToUse = $scope.newImage || $scope.currentImage.split(':')[0]; var tagToUse = $scope.newTag || 'latest'; var data = { containerName: $scope.updateContainerName, newImage: imageToUse, newTag: tagToUse }; var url = '/docker/updateContainer'; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; // Show loading $('#updateContainer').modal('hide'); new PNotify({ title: 'Updating Container', text: 'Please wait while the container is being updated...', type: 'info', hide: false }); $http.post(url, data, config).then(function(response) { if (response.data.updateContainerStatus === 1) { new PNotify({ title: 'Container Updated Successfully', text: response.data.message || 'Container has been updated successfully', type: 'success' }); // Reload containers list $scope.loadContainers(); } else { new PNotify({ title: 'Update Failed', text: response.data.error_message || 'Failed to update container', type: 'error' }); } }, function(error) { new PNotify({ title: 'Update Failed', text: 'Could not connect to server', type: 'error' }); }); }; // Container actions $scope.startContainer = function(containerName) { var data = { containerName: containerName }; var url = '/docker/startContainer'; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(function(response) { if (response.data.startContainerStatus === 1) { new PNotify({ title: 'Container Started', text: 'Container has been started successfully', type: 'success' }); $scope.loadContainers(); } else { new PNotify({ title: 'Start Failed', text: response.data.error_message || 'Failed to start container', type: 'error' }); } }); }; $scope.stopContainer = function(containerName) { var data = { containerName: containerName }; var url = '/docker/stopContainer'; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(function(response) { if (response.data.stopContainerStatus === 1) { new PNotify({ title: 'Container Stopped', text: 'Container has been stopped successfully', type: 'success' }); $scope.loadContainers(); } else { new PNotify({ title: 'Stop Failed', text: response.data.error_message || 'Failed to stop container', type: 'error' }); } }); }; $scope.restartContainer = function(containerName) { var data = { containerName: containerName }; var url = '/docker/restartContainer'; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(function(response) { if (response.data.restartContainerStatus === 1) { new PNotify({ title: 'Container Restarted', text: 'Container has been restarted successfully', type: 'success' }); $scope.loadContainers(); } else { new PNotify({ title: 'Restart Failed', text: response.data.error_message || 'Failed to restart container', type: 'error' }); } }); }; $scope.deleteContainerWithData = function(containerName) { if (confirm('Are you sure you want to delete this container and all its data? This action cannot be undone.')) { var data = { containerName: containerName }; var url = '/docker/deleteContainerWithData'; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(function(response) { if (response.data.deleteContainerWithDataStatus === 1) { new PNotify({ title: 'Container Deleted', text: 'Container and all data have been deleted successfully', type: 'success' }); $scope.loadContainers(); } else { new PNotify({ title: 'Delete Failed', text: response.data.error_message || 'Failed to delete container', type: 'error' }); } }); } }; $scope.deleteContainerKeepData = function(containerName) { if (confirm('Are you sure you want to delete this container but keep the data? The container will be removed but volumes will be preserved.')) { var data = { containerName: containerName }; var url = '/docker/deleteContainerKeepData'; var config = { headers: { 'X-CSRFToken': getCookie('csrftoken') } }; $http.post(url, data, config).then(function(response) { if (response.data.deleteContainerKeepDataStatus === 1) { new PNotify({ title: 'Container Deleted', text: 'Container has been deleted but data has been preserved', type: 'success' }); $scope.loadContainers(); } else { new PNotify({ title: 'Delete Failed', text: response.data.error_message || 'Failed to delete container', type: 'error' }); } }); } }; });