/* ArkHost Hetzner VPS Module JavaScript */ // Floating IP Management Functions function loadFloatingIPStatus() { var content = document.getElementById('floating-ip-content'); if (!content) return; content.innerHTML = '

Loading floating IP information...

'; // Check if floating IP configurable option is enabled ArkHostHetznerVPS_API('GetFloatingIPStatus', false, {}, function(response) { if (response.result === 'success') { displayFloatingIPStatus(response.data); } else { // Server returned a structured error (like permission denied) content.innerHTML = '
' + (response.message || 'Floating IP not available for this service') + '
'; } }, function(error) { // Network/JavaScript error callback var errorMsg = 'Failed to load floating IP information. Please try again later.'; if (error && error.message) { errorMsg = error.message; } content.innerHTML = '
' + errorMsg + '
'; }); } function displayFloatingIPStatus(data) { var content = document.getElementById('floating-ip-content'); if (!content) return; var html = ''; if (!data.hasFloatingIP) { html = '
' + (lang.settings.floatingIP.notAvailable || 'Floating IP not available for this service') + '
'; } else if (!data.floatingIP) { html = '
' + (lang.settings.floatingIP.none || 'No floating IP assigned') + '
'; // Add option to create floating IP if needed (for configurable option customers) html += '
'; html += '

If you need a floating IP, please contact support to provision one for your service.

'; html += '
'; } else { var ip = data.floatingIP; var isAssigned = ip.server && ip.server.id; html += '
'; html += '
'; html += '
Floating IP Details
'; html += '
'; html += '
'; html += '
'; html += '
'; html += 'IP Address: ' + ip.ip + '
'; html += 'Type: ' + ip.type.toUpperCase() + '
'; html += 'Status: ' + (isAssigned ? lang.settings.floatingIP.assigned || 'Assigned' : lang.settings.floatingIP.unassigned || 'Unassigned') + ''; html += '
'; html += '
'; if (isAssigned) { html += '
'; } else { html += '
'; } // Reverse DNS section html += '
'; html += ''; html += '
'; html += ' 0 ? ip.dns_ptr[0].dns_ptr : '') + '" '; html += 'placeholder="' + (lang.settings.floatingIP.reverseDNSPlaceholder || 'example.com') + '">'; html += '
'; html += ''; html += '
'; html += '
'; html += '
'; html += '
'; html += '
'; html += '
'; html += '
'; } content.innerHTML = html; } function assignFloatingIP(floatingIPId) { showConfirm( 'Assign this floating IP to the server?', 'Assign Floating IP', function() { ArkHostHetznerVPS_API('AssignFloatingIP', false, {floating_ip_id: floatingIPId}, function(response) { if (response.result === 'success') { showNotification('Floating IP assigned successfully', 'success'); setTimeout(loadFloatingIPStatus, 2000); } else { showNotification(response.message || 'Failed to assign floating IP', 'error'); } }); } ); } function unassignFloatingIP(floatingIPId) { showConfirm( 'Unassign this floating IP from the server?', 'Unassign Floating IP', function() { ArkHostHetznerVPS_API('UnassignFloatingIP', false, {floating_ip_id: floatingIPId}, function(response) { if (response.result === 'success') { showNotification('Floating IP unassigned successfully', 'success'); setTimeout(loadFloatingIPStatus, 2000); } else { showNotification(response.message || 'Failed to unassign floating IP', 'error'); } }); } ); } function setReverseDNS(floatingIPId, ip) { var input = document.getElementById('reverse-dns-' + floatingIPId); if (!input) return; var hostname = input.value.trim(); ArkHostHetznerVPS_API('SetFloatingIPReverseDNS', false, { floating_ip_id: floatingIPId, ip: ip, dns_ptr: hostname }, function(response) { if (response.result === 'success') { showNotification('Reverse DNS updated successfully', 'success'); } else { showNotification(response.message || 'Failed to update reverse DNS', 'error'); } }); } // Initialize module variables from template var productURL = productURL || ''; var serverInfoInitial = serverInfoInitial || {}; var lang = lang || {}; function confirmStop() { showConfirm(lang.confirm.stop.message, lang.confirm.stop.title, function() { ArkHostHetznerVPS_API('Stop', true); }); return false; } function confirmRestart() { showConfirm(lang.confirm.restart.message, lang.confirm.restart.title, function() { ArkHostHetznerVPS_API('Reboot', true); }); return false; } function confirmShutdown() { showConfirm(lang.confirmShutdownMessage || 'Are you sure you want to shutdown this VPS?', lang.confirmShutdownTitle || 'Shutdown VPS', function() { ArkHostHetznerVPS_API('Shutdown', true); }); return false; } // Rescue Mode functions function enableRescueMode() { showConfirm( lang.confirmEnableRescueMessage || 'Enable rescue mode?', lang.confirmEnableRescueTitle || 'Enable Rescue Mode', function() { ArkHostHetznerVPS_API('Rescue Mode', true, {}, function(data) { if (data.root_password) { showNotification(lang.rescueModeEnabled + data.root_password + ' (' + lang.savePassword + ')', 'success', 10000); } }); } ); return false; } function enableRescueAndReboot() { showConfirm( lang.confirmEnableRescueRebootMessage || 'Enable rescue mode and reboot?', lang.confirmEnableRescueRebootTitle || 'Enable & Reboot', function() { ArkHostHetznerVPS_API('Rescue Mode', true, {}, function(data) { if (data.root_password) { showNotification(lang.rescueModeEnabled + data.root_password + ' (' + lang.savePassword + ')', 'success', 10000); // Wait a moment then reboot setTimeout(function() { ArkHostHetznerVPS_API('Reboot', true); }, 2000); } }); } ); return false; } function disableRescueMode() { showConfirm( lang.confirmDisableRescueMessage || 'Disable rescue mode?', lang.confirmDisableRescueTitle || 'Disable Rescue Mode', function() { ArkHostHetznerVPS_API('Disable Rescue Mode', true); } ); return false; } function resetRootPassword() { showConfirm( lang.confirmResetPasswordMessage || 'Reset root password?', lang.confirmResetPasswordTitle || 'Reset Root Password', function() { ArkHostHetznerVPS_API('Reset root', true, {}, function(data) { if (data.root_password) { // Show password in a special modal or notification var passwordHtml = '
' + '
' + lang.newRootPassword + '
' + '

' + lang.passwordPrompt + '

' + '
' + '' + '
' + '' + '
' + '
' + '

' + lang.savePassword + ' ' + lang.passwordSaved + '

' + '
'; // Create a modal to show the password var modal = document.createElement('div'); modal.className = 'arkhost-confirm-overlay show'; modal.innerHTML = '
' + '
' + lang.confirmResetPasswordTitle + '
' + '
' + passwordHtml + '
' + '
' + '' + '
' + '
'; document.body.appendChild(modal); } }); } ); return false; } function copyToClipboard(elementId) { var copyText = document.getElementById(elementId); copyText.select(); copyText.setSelectionRange(0, 99999); document.execCommand("copy"); showNotification(lang.passwordCopied || 'Password copied!', 'success'); } // Custom notification functions function showNotification(message, type, duration) { type = type || 'success'; duration = duration || 4000; // Remove existing notifications var existing = document.querySelectorAll('.arkhost-notification'); for (var i = 0; i < existing.length; i++) { existing[i].remove(); } // Create notification element var notification = document.createElement('div'); notification.className = 'arkhost-notification ' + type; // Set icon based on type var icon = 'fa-check-circle'; if (type === 'error') icon = 'fa-exclamation-circle'; else if (type === 'warning') icon = 'fa-exclamation-triangle'; else if (type === 'info') icon = 'fa-info-circle'; notification.innerHTML = '' + message + ''; // Add to document document.body.appendChild(notification); // Trigger animation setTimeout(function() { notification.classList.add('show'); }, 100); // Auto remove setTimeout(function() { notification.classList.remove('show'); setTimeout(function() { if (notification.parentElement) { notification.remove(); } }, 300); }, duration); } function showConfirm(message, title, onConfirm) { title = title || 'Confirm Action'; // Remove existing confirms var existing = document.querySelectorAll('.arkhost-confirm-overlay'); for (var i = 0; i < existing.length; i++) { existing[i].remove(); } // Create confirm dialog var overlay = document.createElement('div'); overlay.className = 'arkhost-confirm-overlay'; overlay.innerHTML = '
' + title + '
' + message + '
'; // Add event listeners var cancelBtn = overlay.querySelector('.cancel'); var confirmBtn = overlay.querySelector('.confirm'); cancelBtn.onclick = function() { overlay.classList.remove('show'); setTimeout(function() { if (overlay.parentElement) { overlay.remove(); } }, 300); }; confirmBtn.onclick = function() { overlay.classList.remove('show'); setTimeout(function() { if (overlay.parentElement) { overlay.remove(); } }, 300); if (onConfirm) { onConfirm(); } }; // Close on backdrop click overlay.onclick = function(e) { if (e.target === overlay) { overlay.classList.remove('show'); setTimeout(function() { if (overlay.parentElement) { overlay.remove(); } }, 300); } }; // Add to document and show document.body.appendChild(overlay); setTimeout(function() { overlay.classList.add('show'); }, 100); } // Helper functions for API calls via WHMCS (use existing jQuery) function ArkHostHetznerVPS_API(action, showAlert, params, callback, errorCallback) { if (showAlert === undefined) showAlert = true; if (params === undefined) params = {}; if (typeof params === 'function') { callback = params; params = {}; } var postData = Object.assign({ action: 'productdetails', id: window.serviceId || '', modop: 'custom', a: 'ClientAreaAPI', api: action, token: window.csrfToken || '' // Add CSRF token to prevent session timeouts }, params); // Use existing WHMCS jQuery with proper headers if (typeof jQuery !== 'undefined') { jQuery.post({ url: window.webRoot + '/clientarea.php', data: postData, dataType: 'json', beforeSend: function(xhr) { // Add CSRF token header for additional security xhr.setRequestHeader('X-CSRF-Token', window.csrfToken || ''); }, success: function(data) { // Check for success - WHMCS might return different success indicators var isSuccess = (data.result === 'success') || (data && typeof data === 'object' && !data.error && !data.message) || (Array.isArray(data)) || (data && Object.keys(data).some(key => !isNaN(key))); // Has numeric keys if (isSuccess) { if (showAlert) showNotification(lang.moduleactionsuccess, 'success'); // Call callback if provided if (callback && typeof callback === 'function') { callback(data); } // Handle specific responses if (action === 'List backups') { updateBackupTable(data); } if (action === 'Create backup') { // Refresh backup list after creation setTimeout(function() { ArkHostHetznerVPS_API('List backups', false); }, 1000); } if (action === 'Delete backup') { // Refresh backup list after deletion setTimeout(function() { ArkHostHetznerVPS_API('List backups', false); }, 1000); } if (action === 'Add Firewall rules') { // Clear the form document.getElementById('firewallAction').value = 'ACCEPT'; document.getElementById('firewallPort').value = ''; document.getElementById('firewallProtocol').value = 'ANY'; document.getElementById('firewallSource').value = ''; document.getElementById('firewallNote').value = ''; // Refresh firewall rules after adding setTimeout(function() { ArkHostHetznerVPS_API('Get Firewall rules', false); }, 1000); } if (action === 'Delete Firewall rule') { // Refresh firewall rules after deletion setTimeout(function() { ArkHostHetznerVPS_API('Get Firewall rules', false); }, 1000); } if (action === 'Get Firewall rules') updateFirewallTable(data); if (action === 'ISO Images') updateISOSelect(data.isos || data); if (action === 'List SSH Keys' && callback) callback(data); if (action === 'Graphs') updateGraphsDisplay(data); // Refresh page for server state changes if (['Start', 'Stop', 'Reboot', 'Reinstall'].indexOf(action) !== -1) { setTimeout(function() { location.reload(); }, 2000); } } else { if (showAlert) showNotification(lang.moduleactionfailed + ': ' + (data.message || 'Unknown error'), 'error'); // Hide loading for graphs if (action === 'Graphs') { document.getElementById('graphs-loading').style.display = 'none'; document.getElementById('graphs-container').style.opacity = '1'; } if (errorCallback) errorCallback(data); } }, error: function(xhr, status, error) { if (showAlert) showNotification(lang.moduleactionfailed + ': ' + (lang.networkError || 'Network error'), 'error'); // Hide loading for graphs if (action === 'Graphs') { document.getElementById('graphs-loading').style.display = 'none'; document.getElementById('graphs-container').style.opacity = '1'; } if (errorCallback) errorCallback({error: 'Network error'}); } }); } return false; } function updateBackupTable(data) { var tableBody = document.querySelector('#backupTable tbody'); tableBody.innerHTML = ''; // Handle the Hetzner API response format var backupList = []; // Handle different response formats if (Array.isArray(data)) { // Direct array response backupList = data; } else if (data && typeof data === 'object') { // Check if there are numeric keys (0, 1, 2, etc.) indicating backup array var keys = Object.keys(data); var numericKeys = keys.filter(function(key) { return !isNaN(key); }); if (numericKeys.length > 0) { backupList = numericKeys.map(function(key) { return data[key]; }); } else if (data.result === 'success') { // WHMCS wrapper with result='success' - look for numeric keys excluding 'result' var filteredKeys = keys.filter(function(key) { return !isNaN(key) && key !== 'result'; }); if (filteredKeys.length > 0) { backupList = filteredKeys.map(function(key) { return data[key]; }); } } } if (backupList.length > 0) { backupList.forEach(function(backup) { if (backup && (backup.id || backup.file)) { var row = document.createElement('tr'); // Use created date for Hetzner backups var backupDate = backup.created || backup.date || 'N/A'; if (backupDate !== 'N/A' && backupDate.includes('T')) { // Format ISO date to YYYY-MM-DD backupDate = backupDate.split('T')[0]; } // Determine backup type var backupType = backup.type === 'backup' ? lang.backups.automatic : lang.backups.manual; // Format size (already in GB from API) var sizeStr = 'N/A'; if (backup.size) { sizeStr = backup.size + ' ' + lang.general.gb; } // Status is always available for Hetzner backups var statusBadge = '' + lang.backups.available + ''; // Use backup ID for Hetzner var backupId = backup.id || backup.file; row.innerHTML = '' + backupDate + '' + '' + sizeStr + '' + '' + backupType + '' + '' + statusBadge + '' + '' + '' + '' + ''; tableBody.appendChild(row); } }); } else { tableBody.innerHTML = '' + lang.general.noBackupsFound + ''; } } function createBackup() { showConfirm( lang.confirm.createBackup.message, lang.confirm.createBackup.title, function() { ArkHostHetznerVPS_API('Create backup', true); } ); return false; } function deleteBackup(backupId) { showConfirm( lang.confirm.deleteBackup.message, lang.confirm.deleteBackup.title, function() { ArkHostHetznerVPS_API('Delete backup', true, { image_id: backupId }); } ); return false; } function restoreBackup(backupId) { showConfirm( lang.confirm.restoreBackup.message, lang.confirm.restoreBackup.title, function() { ArkHostHetznerVPS_API('Restore backup', true, { image_id: backupId }); } ); return false; } function updateFirewallTable(data) { var tableBody = document.querySelector('#firewallTable tbody'); if (!tableBody) return; // Clear all rows tableBody.innerHTML = ''; // Handle the API response format var rulesList = []; // Handle different response formats if (Array.isArray(data)) { rulesList = data; } else if (data && typeof data === 'object') { var keys = Object.keys(data); var numericKeys = keys.filter(function(key) { return !isNaN(key); }); if (numericKeys.length > 0) { rulesList = numericKeys.map(function(key) { return data[key]; }); } else if (data.rules && Array.isArray(data.rules)) { rulesList = data.rules; } else if (data.result === 'success') { var filteredKeys = keys.filter(function(key) { return !isNaN(key) && key !== 'result'; }); if (filteredKeys.length > 0) { rulesList = filteredKeys.map(function(key) { return data[key]; }); } } } // Add firewall rules if any exist if (rulesList.length > 0) { rulesList.forEach(function(rule) { if (rule && rule.id) { var actionBadge; if (rule.action === 'ACCEPT') { actionBadge = '' + lang.firewall.accept + ''; } else if (rule.action === 'DROP') { actionBadge = '' + lang.firewall.drop + ''; } else if (rule.action === 'INFO') { actionBadge = '' + lang.firewall.info + ''; } else { actionBadge = '' + rule.action + ''; } var protocolBadge = '' + (rule.protocol || lang.firewall.any) + ''; var row = document.createElement('tr'); row.innerHTML = '' + actionBadge + '' + '' + protocolBadge + '' + '' + (rule.port || '' + lang.general.any + '') + '' + '' + (rule.source || '0.0.0.0/0') + '' + '' + (rule.note || '' + lang.general.emptyValue + '') + '' + '' + (rule.id !== 'info' ? '' : '' + lang.general.emptyValue + '') + ''; tableBody.appendChild(row); } }); } else { tableBody.innerHTML = '' + lang.general.noFirewallRules + ''; } } function addFirewallRule() { var action = document.getElementById('firewallAction').value; var protocol = document.getElementById('firewallProtocol').value; var port = document.getElementById('firewallPort').value; var source = document.getElementById('firewallSource').value || '0.0.0.0/0'; var note = document.getElementById('firewallNote').value; // Validate port for TCP/UDP if ((protocol === 'TCP' || protocol === 'UDP') && !port) { showNotification(lang.messages.portRequired, 'error'); document.getElementById('firewallPort').focus(); return false; } ArkHostHetznerVPS_API('Add Firewall rules', true, { firewallAction: action, protocol: protocol, source: source, port: port, note: note }); return false; } function deleteFirewallRule(ruleId) { showConfirm( lang.confirmDeleteFirewallRuleMessage || 'Delete this firewall rule?', lang.confirmDeleteFirewallRuleTitle || 'Delete Firewall Rule', function() { ArkHostHetznerVPS_API('Delete Firewall rule', true, { rule_id: ruleId }); } ); return false; } function updateISOSelect(data) { var select = document.getElementById('isoID'); if (!select) return; // Clear existing options except the first one select.innerHTML = ''; // Function to check if a value looks like a valid ISO function isValidISO(value, name) { if (!value || !name) return false; // Convert to strings for comparison var valueStr = String(value).toLowerCase(); var nameStr = String(name).toLowerCase(); // Filter out common non-ISO values var invalidValues = ['success', 'error', 'status', 'result', 'message', 'data']; if (invalidValues.includes(valueStr) || invalidValues.includes(nameStr)) { return false; } // For Hetzner, if it has an ID and name, it's valid return true; } // Handle different possible response formats from Hetzner API if (data) { // Format 1: Array of ISO objects (Hetzner format) if (Array.isArray(data)) { data.forEach(function(iso) { if (iso && (iso.id || iso.name)) { // For Hetzner, ISOs are identified by name, not ID var isoValue = iso.name || iso.id; var isoName = iso.description || iso.name || 'ISO ' + iso.id; // Add architecture info if available if (iso.architecture) { isoName += ' (' + iso.architecture.toUpperCase() + ')'; } if (isValidISO(isoValue, isoName)) { var option = document.createElement('option'); option.value = isoValue; option.textContent = isoName; select.appendChild(option); } } }); } // Format 2: Object with ISO IDs as keys else if (typeof data === 'object') { Object.keys(data).forEach(function(key) { var iso = data[key]; if (iso && typeof iso === 'object') { var isoValue = iso.id || iso.iso_id || iso.filename || key; var isoName = iso.name || iso.filename || iso.label || key; if (isValidISO(isoValue, isoName)) { var option = document.createElement('option'); option.value = isoValue; option.textContent = isoName; select.appendChild(option); } } else if (typeof iso === 'string') { // Simple key-value format if (isValidISO(key, iso)) { var option = document.createElement('option'); option.value = key; option.textContent = iso; select.appendChild(option); } } }); } } // If no ISOs were added, show a message if (select.options.length === 1) { var option = document.createElement('option'); option.value = ''; option.textContent = lang.noISOImages || 'No ISO images available'; option.disabled = true; select.appendChild(option); } } // Helper function to resize graph images function resizeGraphImage(htmlContent) { if (htmlContent.includes(']*?)>/g, ''); } return htmlContent; } // Simple chart creation functions function createSimpleLineChart(data, labels, title, color) { if (!data || data.length === 0) { return '

' + (lang.general.noDataAvailable || 'No data available') + '

'; } var max = Math.max(...data); // Ensure max is at least 100 for CPU percentage if (title.includes('CPU') && max < 100) max = 100; if (max < 1) max = 1; var html = '
'; html += '
' + title + '
'; html += '
'; // Add Y-axis labels container html += '
'; for (var i = 4; i >= 0; i--) { var value = (max / 4) * i; html += '' + Math.round(value) + '%'; } html += '
'; // Chart area with left margin for labels html += '
'; html += ''; // Grid lines for (var i = 0; i <= 4; i++) { var y = i * 50; html += ''; } // Add some vertical grid lines for time reference for (var i = 0; i <= 5; i++) { var x = i * 100; html += ''; } // Draw line var points = ''; for (var i = 0; i < data.length; i++) { var x = (i / (data.length - 1)) * 500; var y = 200 - (data[i] / max) * 190; points += x + ',' + y + ' '; } html += ''; // Add dots on data points for better visibility for (var i = 0; i < data.length; i++) { var x = (i / (data.length - 1)) * 500; var y = 200 - (data[i] / max) * 190; html += ''; } html += ''; html += '
'; // Time labels at bottom html += '
'; if (labels && labels.length > 0) { var step = Math.ceil(labels.length / 6); for (var i = 0; i < labels.length; i += step) { html += '' + labels[i] + ''; } html += '' + labels[labels.length - 1] + ''; } html += '
'; // Current value html += '
'; html += '' + (lang.graphs.current || 'Current') + lang.general.colon + Math.round(data[data.length - 1]) + lang.graphs.cpuUnit + ''; html += '
'; return html; } function createDoubleLineChart(data1, data2, labels, title, label1, label2, color1, color2) { if (!data1 || data1.length === 0) { return '

' + (lang.general.noDataAvailable || 'No data available') + '

'; } var max = Math.max(...data1, ...data2); // Ensure minimum scale for better visibility if (max < 1) max = 1; // Format function for values based on title var formatValue = function(val) { if (title.includes(lang.graphs.networkUnit)) { return val.toFixed(2) + ' ' + lang.graphs.networkUnit; } else if (title.includes(lang.graphs.diskUnit)) { return Math.round(val) + ' ' + lang.graphs.diskUnit; } return val.toFixed(1); }; var html = '
'; html += '
' + title + '
'; html += '
'; // Add Y-axis labels container html += '
'; for (var i = 4; i >= 0; i--) { var value = (max / 4) * i; html += '' + (value >= 1 ? Math.round(value) : value.toFixed(2)) + ''; } html += '
'; // Chart area with left margin for labels html += '
'; html += ''; // Grid lines for (var i = 0; i <= 4; i++) { var y = i * 50; html += ''; } // Add some vertical grid lines for time reference for (var i = 0; i <= 5; i++) { var x = i * 100; html += ''; } // Line 1 var points1 = ''; for (var i = 0; i < data1.length; i++) { var x = (i / (data1.length - 1)) * 500; var y = 200 - (data1[i] / max) * 190; points1 += x + ',' + y + ' '; } html += ''; // Line 2 var points2 = ''; for (var i = 0; i < data2.length; i++) { var x = (i / (data2.length - 1)) * 500; var y = 200 - (data2[i] / max) * 190; points2 += x + ',' + y + ' '; } html += ''; html += ''; html += '
'; // Time labels at bottom html += '
'; if (labels && labels.length > 0) { var step = Math.ceil(labels.length / 6); for (var i = 0; i < labels.length; i += step) { html += '' + labels[i] + ''; } html += '' + labels[labels.length - 1] + ''; } html += '
'; // Legend with current values html += '
'; html += '' + label1 + lang.general.colon + formatValue(data1[data1.length - 1]) + ''; html += '' + label2 + lang.general.colon + formatValue(data2[data2.length - 1]) + ''; html += '
'; return html; } function updateGraphsDisplay(data) { // Hide loading indicator document.getElementById('graphs-loading').style.display = 'none'; document.getElementById('graphs-container').style.opacity = '1'; // Update graphs if data is available if (data.graphs && data.graphs.type === 'metrics' && data.graphs.data) { var metricsData = data.graphs.data; // Display CPU graph if (metricsData.cpu) { var cpuHtml = createSimpleLineChart(metricsData.cpu.data, metricsData.cpu.labels, lang.graphs.cpuUsage + ' (' + lang.graphs.cpuUnit + ')', '#e74c3c'); document.getElementById('cpu-graph').innerHTML = cpuHtml; } // Display Disk I/O graph if (metricsData.disk) { var diskHtml = createDoubleLineChart( metricsData.disk.read, metricsData.disk.write, metricsData.disk.labels, lang.graphs.diskIO + ' (' + lang.graphs.diskUnit + ')', lang.graphs.read || 'Read', lang.graphs.write || 'Write', '#f39c12', '#e67e22' ); document.getElementById('disk-graph').innerHTML = diskHtml; } // Display Network graph if (metricsData.network) { var networkHtml = createDoubleLineChart( metricsData.network.in, metricsData.network.out, metricsData.network.labels, lang.graphs.networkTraffic + ' (' + lang.graphs.networkUnit + ')', lang.graphs.inbound || 'Inbound', lang.graphs.outbound || 'Outbound', '#3498db', '#2980b9' ); document.getElementById('network-graph').innerHTML = networkHtml; } } else if (data.graphs && data.graphs.type === 'none') { // No metrics available var noMetricsMessage = '

' + (data.graphs.message || 'Metrics not available') + '

'; document.getElementById('cpu-graph').innerHTML = noMetricsMessage; document.getElementById('network-graph').innerHTML = noMetricsMessage; document.getElementById('disk-graph').innerHTML = noMetricsMessage; } else { // Fallback document.getElementById('cpu-graph').innerHTML = '

Metrics data format not recognized

'; document.getElementById('network-graph').innerHTML = '

Metrics data format not recognized

'; document.getElementById('disk-graph').innerHTML = '

Metrics data format not recognized

'; } } // Load initial graphs when tab is first opened var graphsLoaded = false; function loadInitialGraphs() { if (!graphsLoaded) { setTimeout(function() { loadGraphs('day', document.querySelector('.graph-time-btn.active')); graphsLoaded = true; }, 100); } } // Load backups when tab is first opened var backupsLoaded = false; function loadBackups() { if (!backupsLoaded) { // Show loading state var tableBody = document.querySelector('#backupTable tbody'); if (tableBody) { tableBody.innerHTML = ' ' + (lang.loadingBackups || 'Loading backups...') + ''; } setTimeout(function() { ArkHostHetznerVPS_API('List backups', false); backupsLoaded = true; }, 100); } } // Function to load graphs for different time periods function loadGraphs(timePeriod, buttonElement) { // Update active button document.querySelectorAll('.graph-time-btn').forEach(btn => btn.classList.remove('active')); buttonElement.classList.add('active'); // Show loading indicator document.getElementById('graphs-loading').style.display = 'block'; document.getElementById('graphs-container').style.opacity = '0.5'; // Use the existing ArkHostHetznerVPS_API function which now handles sessions properly ArkHostHetznerVPS_API('Graphs', false, { time: timePeriod }); } // Function to refresh all graphs with current time period function refreshAllGraphs() { // Find the currently active time period button var activeButton = document.querySelector('.graph-time-btn.active'); if (activeButton) { // Show notification showNotification(lang.messages.refreshingGraphs, 'info'); // Reload graphs with current time period activeButton.click(); } } function ArkHostVPS_ChooseOS(element) { if (typeof jQuery !== 'undefined') { var osId = jQuery(element).data('os'); var group = jQuery(element).data('group'); jQuery('#newOS').val(osId); // FIRST: Reset ALL OS selections (clear all groups) jQuery('.os_badge .dropdown-toggle').removeClass('btn-success').addClass('btn-outline-secondary'); jQuery('.os_badge .dropdown-item').removeClass('active bg-primary text-white'); jQuery('.os_badge .version .fa-check').remove(); jQuery('.os_badge .version').each(function() { var originalText = jQuery(this).text().replace(' ✓', '').replace(/\s*]*><\/i>\s*/g, ''); jQuery(this).text(originalText); }); // THEN: Set the selected OS // Update the version text with the selected OS jQuery('#' + group + '-version').text(jQuery(element).text()); // Add active class to the selected item jQuery(element).addClass('active bg-primary text-white'); // Update the main button to show selected state var mainButton = jQuery('#' + group + '-os .dropdown-toggle'); mainButton.removeClass('btn-outline-secondary').addClass('btn-success'); // Add checkmark to show selection var versionSpan = jQuery('#' + group + '-version'); versionSpan.append(' '); // No advanced options needed for Hetzner rebuild } return false; } function ArkHostVPS_ShowPassword() { if (typeof jQuery !== 'undefined') { var input = jQuery('#vpsPassword'); var icon = jQuery('#showPasswordIcon'); if (input.attr('type') === 'password') { input.attr('type', 'text'); icon.removeClass('fa-eye').addClass('fa-eye-slash'); } else { input.attr('type', 'password'); icon.removeClass('fa-eye-slash').addClass('fa-eye'); } } return false; } function reinstallWithAdvancedOptions() { var osId = document.getElementById('newOS').value; if (!osId || osId === '0') { showNotification(lang.messages.selectOSFirst, 'warning'); return false; } // Hetzner rebuild only needs the image parameter var params = { os: osId }; // Call the API ArkHostHetznerVPS_API('Reinstall', true, params); return false; }