From 394f7e8af8b0517b7b4ae2d53673964a20c2e34d Mon Sep 17 00:00:00 2001 From: ArkHost <150489165+ARKHOST21@users.noreply.github.com> Date: Mon, 21 Jul 2025 15:25:29 +0200 Subject: [PATCH] v1.0 Initial release --- .../ArkHostHetznerVPS/ArkHostHetznerVPS.php | 2423 +++++++++++++++++ .../servers/ArkHostHetznerVPS/lang/dutch.php | 231 ++ .../ArkHostHetznerVPS/lang/english.php | 248 ++ .../servers/ArkHostHetznerVPS/lang/french.php | 231 ++ .../servers/ArkHostHetznerVPS/lang/german.php | 231 ++ .../ArkHostHetznerVPS/lang/italian.php | 231 ++ .../ArkHostHetznerVPS/lang/portuguese-pt.php | 248 ++ .../ArkHostHetznerVPS/lang/russian.php | 231 ++ .../ArkHostHetznerVPS/lang/spanish.php | 231 ++ .../template/clientarea_direct.tpl | 756 +++++ .../ArkHostHetznerVPS/template/img/cloud.png | Bin 0 -> 3395 bytes .../ArkHostHetznerVPS/template/img/edit.png | Bin 0 -> 3453 bytes .../ArkHostHetznerVPS/template/img/eye.png | Bin 0 -> 3661 bytes .../template/img/installing.png | Bin 0 -> 3968 bytes .../template/img/loading.png | Bin 0 -> 3511 bytes .../ArkHostHetznerVPS/template/img/lock.png | Bin 0 -> 3443 bytes .../ArkHostHetznerVPS/template/img/notice.png | Bin 0 -> 269 bytes .../template/img/os/almalinux.png | Bin 0 -> 11260 bytes .../template/img/os/centos.png | Bin 0 -> 3462 bytes .../template/img/os/debian.png | Bin 0 -> 3571 bytes .../template/img/os/fedora.png | Bin 0 -> 3239 bytes .../ArkHostHetznerVPS/template/img/os/iso.png | Bin 0 -> 2754 bytes .../template/img/os/lubuntu.png | Bin 0 -> 3287 bytes .../template/img/os/mikrotik.png | Bin 0 -> 3601 bytes .../template/img/os/opensuse.png | Bin 0 -> 2953 bytes .../template/img/os/others.png | Bin 0 -> 2796 bytes .../template/img/os/rockylinux.png | Bin 0 -> 2265 bytes .../template/img/os/ubuntu.png | Bin 0 -> 3351 bytes .../template/img/os/windows.png | Bin 0 -> 2149 bytes .../template/img/running.png | Bin 0 -> 3603 bytes .../ArkHostHetznerVPS/template/img/search.png | Bin 0 -> 3724 bytes .../template/img/settings.png | Bin 0 -> 3693 bytes .../template/img/stopped.png | Bin 0 -> 3513 bytes .../template/img/suspended.png | Bin 0 -> 3368 bytes .../template/img/under-cancellation.png | Bin 0 -> 3358 bytes .../ArkHostHetznerVPS/template/img/vnc.png | Bin 0 -> 2970 bytes .../ArkHostHetznerVPS/template/script.js | 1126 ++++++++ .../ArkHostHetznerVPS/template/style.css | 604 ++++ 38 files changed, 6791 insertions(+) create mode 100644 modules/servers/ArkHostHetznerVPS/ArkHostHetznerVPS.php create mode 100644 modules/servers/ArkHostHetznerVPS/lang/dutch.php create mode 100644 modules/servers/ArkHostHetznerVPS/lang/english.php create mode 100644 modules/servers/ArkHostHetznerVPS/lang/french.php create mode 100644 modules/servers/ArkHostHetznerVPS/lang/german.php create mode 100644 modules/servers/ArkHostHetznerVPS/lang/italian.php create mode 100644 modules/servers/ArkHostHetznerVPS/lang/portuguese-pt.php create mode 100644 modules/servers/ArkHostHetznerVPS/lang/russian.php create mode 100644 modules/servers/ArkHostHetznerVPS/lang/spanish.php create mode 100644 modules/servers/ArkHostHetznerVPS/template/clientarea_direct.tpl create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/cloud.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/edit.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/eye.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/installing.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/loading.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/lock.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/notice.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/os/almalinux.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/os/centos.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/os/debian.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/os/fedora.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/os/iso.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/os/lubuntu.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/os/mikrotik.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/os/opensuse.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/os/others.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/os/rockylinux.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/os/ubuntu.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/os/windows.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/running.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/search.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/settings.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/stopped.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/suspended.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/under-cancellation.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/img/vnc.png create mode 100644 modules/servers/ArkHostHetznerVPS/template/script.js create mode 100644 modules/servers/ArkHostHetznerVPS/template/style.css diff --git a/modules/servers/ArkHostHetznerVPS/ArkHostHetznerVPS.php b/modules/servers/ArkHostHetznerVPS/ArkHostHetznerVPS.php new file mode 100644 index 0000000..59e66c5 --- /dev/null +++ b/modules/servers/ArkHostHetznerVPS/ArkHostHetznerVPS.php @@ -0,0 +1,2423 @@ + + * @link https://arkhost.com + */ + +if (!defined('WHMCS')) { + http_response_code(403); + exit('Access denied'); +} + +use WHMCS\Config\Setting; +use WHMCS\Database\Capsule; + +function ArkHostHetznerVPS_GetVPSID(array $params) { + // Check if we have model (new way) - ArkHostHetznerVPS format + if (isset($params['model']) && is_object($params['model']) && isset($params['model']->serviceProperties)) { + // Try our new format first + $vpsId = $params['model']->serviceProperties->get('ArkHostHetznerVPS|VPS ID'); + if ($vpsId) { + return str_replace(['ArkHostHetznerVPS-', 'VPS-'], '', $vpsId); + } + + // Try ModulesGarden format for migration compatibility + $serverId = $params['model']->serviceProperties->get('serverID|Server ID'); + if ($serverId) { + return str_replace(['server-', 'Server-'], '', $serverId); + } + } + + // Check if we have customfields (alternative way) + if (isset($params['customfields']) && is_array($params['customfields'])) { + foreach ($params['customfields'] as $field => $value) { + // Check for our new format: ArkHostHetznerVPS|VPS ID + if (stripos($field, 'ArkHostHetznerVPS') !== false && stripos($field, 'VPS ID') !== false) { + return str_replace(['ArkHostHetznerVPS-', 'VPS-'], '', $value); + } + + // Check for ModulesGarden format: serverID|Server ID (MIGRATION COMPATIBILITY) + if (stripos($field, 'serverID') !== false && stripos($field, 'Server ID') !== false) { + return str_replace(['server-', 'Server-'], '', $value); + } + + // Fallback: any field containing VPS ID + if (stripos($field, 'VPS ID') !== false && !empty($value)) { + return str_replace(['ArkHostHetznerVPS-', 'VPS-', 'server-', 'Server-'], '', $value); + } + } + } + + // For ModulesGarden migration - check if server ID is in domain field + if (isset($params['domain']) && is_numeric($params['domain'])) { + return $params['domain']; + } + + // For ModulesGarden migration - check username field + if (isset($params['username']) && is_numeric($params['username'])) { + return $params['username']; + } + + // Check customfields for any numeric server ID (broader compatibility) + if (isset($params['customfields']) && is_array($params['customfields'])) { + foreach ($params['customfields'] as $field => $value) { + // ModulesGarden might store it as "Server ID" or similar + if ((stripos($field, 'server') !== false || stripos($field, 'vps') !== false) && is_numeric($value)) { + return $value; + } + } + } + + // Check if there's a dedicatedip field with server ID (some modules use this) + if (isset($params['dedicatedip']) && preg_match('/(\d{6,})/', $params['dedicatedip'], $matches)) { + return $matches[1]; + } + + return null; +} + +function ArkHostHetznerVPS_GetConfigurableOption(array $params, $optionName) { + // Check for WHMCS Configurable Options (add-ons) that customer has ordered + if (isset($params['configoptions']) && is_array($params['configoptions'])) { + foreach ($params['configoptions'] as $optName => $optValue) { + // Check if option name contains the floating IP identifier + if (stripos($optName, $optionName) !== false && !empty($optValue)) { + return $optValue; + } + } + } + + // Also check by option ID if available + if (isset($params['configoptionshash']) && is_array($params['configoptionshash'])) { + foreach ($params['configoptionshash'] as $optId => $optValue) { + if (stripos($optId, $optionName) !== false && !empty($optValue)) { + return $optValue; + } + } + } + + return null; +} + +function ArkHostHetznerVPS_API(array $params) { + $url = 'https://api.hetzner.cloud/v1/'; + $data = []; + $method = ''; + + switch ($params['action']) { + case 'Test': + $url .= 'server_types?per_page=1'; + $method = 'GET'; + break; + + case 'Packages': + $url .= 'server_types'; + $method = 'GET'; + break; + + case 'Operating Systems': + $url .= 'images?type=system'; + $method = 'GET'; + break; + + case 'Datacenters': + $url .= 'datacenters'; + $method = 'GET'; + break; + + case 'Upgrades': + // Not supported in Hetzner API + return array(); + break; + + case 'Discount': + // Hetzner doesn't have a discount endpoint, return dummy data + return array('percent' => 0); + break; + + case 'Balance': + // Hetzner doesn't have a balance endpoint, return dummy data + return array('balance' => 'N/A'); + break; + + case 'Order': + $url .= 'servers'; + $method = 'POST'; + + $data = array( + 'name' => $params['domain'] ?? 'vps-' . time(), + 'server_type' => ArkHostHetznerVPS_GetOption($params, 'planid'), + 'image' => ArkHostHetznerVPS_GetOption($params, 'osid'), + 'start_after_create' => true, + 'labels' => array( + 'whmcs_service_id' => (string)$params['serviceid'], + 'whmcs_user_id' => (string)$params['userid'] + ) + ); + + // Add datacenter if specified + $datacenter = ArkHostHetznerVPS_GetOption($params, 'datacenter'); + if ($datacenter) { + $data['datacenter'] = $datacenter; + } + + // Handle backups + if (ArkHostHetznerVPS_GetOption($params, 'backups') === 'on') { + $data['automount'] = false; + $data['backups'] = true; + } + + // Handle IPv6 + if (ArkHostHetznerVPS_GetOption($params, 'ipv6') === 'on') { + $data['enable_ipv6'] = false; + } + + // Handle SSH keys from custom field if needed + // This would need to be implemented with a custom field + break; + + case 'Server Info': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params); + $method = 'GET'; + break; + + case 'Label': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params); + $method = 'PUT'; + $data = array( + 'name' => $params['label'], + ); + break; + + case 'Graphs': + // Hetzner metrics endpoint requires specific format + $serverId = ArkHostHetznerVPS_GetVPSID($params); + + // Determine time range based on period + $period = $params['time'] ?? 'day'; + $end = time(); + switch ($period) { + case 'hour': + $start = $end - 3600; // 1 hour + $step = 60; // 1 minute intervals + break; + case 'day': + $start = $end - 86400; // 24 hours + $step = 300; // 5 minute intervals + break; + case 'week': + $start = $end - 604800; // 7 days + $step = 1800; // 30 minute intervals + break; + case 'month': + $start = $end - 2592000; // 30 days + $step = 7200; // 2 hour intervals + break; + case 'year': + $start = $end - 31536000; // 365 days + $step = 86400; // 1 day intervals + break; + default: + $start = $end - 86400; + $step = 300; + } + + // Get all metric types at once + $metricType = 'cpu,disk,network'; + + // Format timestamps in ISO-8601 - Hetzner requires specific format without timezone + $startISO = gmdate('Y-m-d\TH:i:s\Z', $start); + $endISO = gmdate('Y-m-d\TH:i:s\Z', $end); + + $url .= 'servers/' . $serverId . '/metrics?type=' . $metricType . '&start=' . $startISO . '&end=' . $endISO . '&step=' . $step; + $method = 'GET'; + break; + + case 'Operating Systems - Server': + $url .= 'images?type=system'; + $method = 'GET'; + break; + + case 'Cancel': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params); + $method = 'DELETE'; + break; + + case 'Stop Cancellation': + // Not supported in Hetzner API + return array('success' => true); + break; + + case 'VNC Console': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/request_console'; + $method = 'POST'; + // No data needed for request_console + break; + + case 'Reinstall': + // Rebuild server with new image (destroys all data) + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/rebuild'; + $method = 'POST'; + $data = array( + 'image' => $params['os'] // Can be image ID or name (e.g. "ubuntu-20.04") + ); + break; + + case 'Reboot': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/reboot'; + $method = 'POST'; + break; + + case 'Stop': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/poweroff'; + $method = 'POST'; + break; + + case 'Shutdown': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/shutdown'; + $method = 'POST'; + break; + + case 'Start': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/poweron'; + $method = 'POST'; + break; + + case 'Disable': + // Use poweroff for disable in Hetzner + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/poweroff'; + $method = 'POST'; + break; + + case 'Enable': + // Use poweron for enable in Hetzner + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/poweron'; + $method = 'POST'; + break; + + case 'IPv4 Addresses': + // Get server info which includes IP addresses + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params); + $method = 'GET'; + break; + + case 'Reverse DNS': + // Hetzner handles rDNS differently + return array('success' => false, 'message' => 'Not implemented'); + break; + + case 'Addons': + // Not supported in Hetzner API + return array(); + break; + + case 'Upgrade': + // Hetzner requires creating a new server + return array('success' => false, 'message' => 'Server upgrades not supported'); + break; + + case 'Hostname rDNS': + // Update server name + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params); + $method = 'PUT'; + $data = array( + 'name' => $params['hostname'] + ); + break; + + case 'Create backup': + // Use snapshots + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/create_image'; + $method = 'POST'; + $data = array( + 'description' => 'Backup created on ' . date('Y-m-d H:i:s'), + 'type' => 'backup' + ); + break; + + case 'Delete backup': + // Delete image + $imageId = $params['image_id'] ?? $params['file'] ?? ''; + $url .= 'images/' . $imageId; + $method = 'DELETE'; + break; + + case 'List backups': + // List images of type backup + $url .= 'images?type=backup&bound_to=' . ArkHostHetznerVPS_GetVPSID($params); + $method = 'GET'; + break; + + case 'Restore backup': + // Rebuild from image + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/rebuild'; + $method = 'POST'; + $data = array( + 'image' => $params['image_id'] ?? $params['file'] ?? '' + ); + break; + + case 'Get Firewall rules': + // Get server info first to find attached firewalls + $serverId = ArkHostHetznerVPS_GetVPSID($params); + $url .= 'servers/' . $serverId; + $method = 'GET'; + break; + + case 'List Firewalls': + // List all available firewalls + $url .= 'firewalls'; + $method = 'GET'; + break; + + case 'Create Firewall': + $url .= 'firewalls'; + $method = 'POST'; + $data = array( + 'name' => $params['name'] ?? 'firewall-' . time(), + 'rules' => isset($params['rules']) ? $params['rules'] : array() + ); + break; + + case 'Apply Firewall': + $url .= 'firewalls/' . $params['firewall_id'] . '/actions/apply_to_resources'; + $method = 'POST'; + $data = array( + 'apply_to' => array( + array( + 'type' => 'server', + 'server' => ArkHostHetznerVPS_GetVPSID($params) + ) + ) + ); + break; + case 'Get Firewall Details': + $url .= 'firewalls/' . $params['firewall_id']; + $method = 'GET'; + break; + case 'Update Firewall Rules': + $url .= 'firewalls/' . $params['firewall_id'] . '/actions/set_rules'; + $method = 'POST'; + $data = array( + 'rules' => $params['rules'] + ); + break; + + case 'Remove Firewall': + $url .= 'firewalls/' . $params['firewall_id'] . '/actions/remove_from_resources'; + $method = 'POST'; + $data = array( + 'remove_from' => array( + array( + 'type' => 'server', + 'server' => ArkHostHetznerVPS_GetVPSID($params) + ) + ) + ); + break; + + case 'Add Firewall rules': + // For Hetzner, we need to check if a firewall exists, create one if not, then update rules + $serverId = ArkHostHetznerVPS_GetVPSID($params); + + // First, get server info to check if firewall is attached + $serverParams = $params; + $serverParams['action'] = 'Server Info'; + $serverInfo = ArkHostHetznerVPS_API($serverParams); + + $firewallId = null; + if (isset($serverInfo['server']['public_net']['firewalls']) && !empty($serverInfo['server']['public_net']['firewalls'])) { + // Use existing firewall + $firewallId = $serverInfo['server']['public_net']['firewalls'][0]['id']; + + // Get current firewall rules + $firewallParams = $params; + $firewallParams['action'] = 'Get Firewall Details'; + $firewallParams['firewall_id'] = $firewallId; + $firewallDetails = ArkHostHetznerVPS_API($firewallParams); + + $existingRules = isset($firewallDetails['firewall']['rules']) ? $firewallDetails['firewall']['rules'] : array(); + } else { + // No firewall attached, create one + $createParams = $params; + $createParams['action'] = 'Create Firewall'; + $createParams['name'] = 'firewall-server-' . $serverId; + $createParams['rules'] = array(); + + $createResult = ArkHostHetznerVPS_API($createParams); + $firewallId = $createResult['firewall']['id']; + + // Attach firewall to server + $attachParams = $params; + $attachParams['action'] = 'Apply Firewall'; + $attachParams['firewall_id'] = $firewallId; + ArkHostHetznerVPS_API($attachParams); + + $existingRules = array(); + } + + // Add new rule to existing rules + $newRule = array( + 'direction' => 'in', + 'protocol' => strtolower($params['protocol']), + ); + + // Handle source IPs - ensure CIDR notation + if (!empty($params['source']) && $params['source'] !== '0.0.0.0/0') { + $sourceIp = $params['source']; + // If no CIDR notation, add /32 for IPv4 or /128 for IPv6 + if (strpos($sourceIp, '/') === false) { + if (filter_var($sourceIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + $sourceIp .= '/128'; + } else { + $sourceIp .= '/32'; + } + } + $newRule['source_ips'] = array($sourceIp); + } else { + $newRule['source_ips'] = array(); + } + + // Handle port + if (!empty($params['port']) && $params['protocol'] !== 'icmp') { + $newRule['port'] = $params['port']; + } + + // Add description if provided + if (!empty($params['note'])) { + $newRule['description'] = $params['note']; + } + + // Only add ACCEPT rules (Hetzner default is DROP for non-matched) + if ($params['firewallAction'] === 'ACCEPT') { + $existingRules[] = $newRule; + } + + // Update firewall with new rules + $updateParams = $params; + $updateParams['action'] = 'Update Firewall Rules'; + $updateParams['firewall_id'] = $firewallId; + $updateParams['rules'] = $existingRules; + + ArkHostHetznerVPS_API($updateParams); + + return array('success' => true, 'message' => 'Firewall rule added successfully'); + break; + + case 'Delete Firewall rule': + // For Hetzner, we need to get all rules, remove the one, and update + $serverId = ArkHostHetznerVPS_GetVPSID($params); + + // Get server info to find attached firewall + $serverParams = $params; + $serverParams['action'] = 'Server Info'; + $serverInfo = ArkHostHetznerVPS_API($serverParams); + + if (!isset($serverInfo['server']['public_net']['firewalls']) || empty($serverInfo['server']['public_net']['firewalls'])) { + return array('success' => false, 'message' => 'No firewall attached to this server'); + } + + $firewallId = $serverInfo['server']['public_net']['firewalls'][0]['id']; + + // Get current firewall rules + $firewallParams = $params; + $firewallParams['action'] = 'Get Firewall Details'; + $firewallParams['firewall_id'] = $firewallId; + $firewallDetails = ArkHostHetznerVPS_API($firewallParams); + + $existingRules = isset($firewallDetails['firewall']['rules']) ? $firewallDetails['firewall']['rules'] : array(); + $newRules = array(); + + // Remove the rule with matching ID (we use index as ID) + $ruleIdToDelete = $params['rule_id']; + $index = 0; + foreach ($existingRules as $rule) { + if ($rule['direction'] === 'in') { + if ('fw_' . $firewallId . '_' . $index != $ruleIdToDelete) { + $newRules[] = $rule; + } + $index++; + } else { + // Keep outbound rules + $newRules[] = $rule; + } + } + + // Update firewall with remaining rules + $updateParams = $params; + $updateParams['action'] = 'Update Firewall Rules'; + $updateParams['firewall_id'] = $firewallId; + $updateParams['rules'] = $newRules; + + ArkHostHetznerVPS_API($updateParams); + + return array('success' => true, 'message' => 'Firewall rule deleted successfully'); + break; + + case 'Commit Firewall rules': + // Not applicable for Hetzner - changes are immediate + return array('success' => true, 'message' => 'Firewall changes are applied immediately in Hetzner'); + break; + + case 'ISO Images': + $url .= 'isos'; + $method = 'GET'; + break; + + case 'Load ISO': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/attach_iso'; + $method = 'POST'; + // Hetzner expects the ISO name, not ID + $data = array('iso' => $params['iso_id']); + break; + + case 'Eject ISO': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/detach_iso'; + $method = 'POST'; + break; + + case 'Reset root': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/reset_password'; + $method = 'POST'; + break; + + case 'Server Metrics': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/metrics'; + $method = 'GET'; + break; + + case 'Create Snapshot': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/create_image'; + $method = 'POST'; + $data = array( + 'description' => $params['description'] ?? 'Snapshot created on ' . date('Y-m-d H:i:s'), + 'type' => 'snapshot' + ); + break; + + case 'List Snapshots': + $url .= 'images?type=snapshot&bound_to=' . ArkHostHetznerVPS_GetVPSID($params); + $method = 'GET'; + break; + + case 'Floating IPs': + $url .= 'floating_ips'; + $method = 'GET'; + break; + + case 'Get Floating IP': + $url .= 'floating_ips/' . $params['floating_ip_id']; + $method = 'GET'; + break; + + case 'Assign Floating IP': + $url .= 'floating_ips/' . $params['floating_ip_id'] . '/actions/assign'; + $method = 'POST'; + $data = array( + 'server' => ArkHostHetznerVPS_GetVPSID($params) + ); + break; + + case 'Unassign Floating IP': + $url .= 'floating_ips/' . $params['floating_ip_id'] . '/actions/unassign'; + $method = 'POST'; + break; + + case 'Create Floating IP': + $url .= 'floating_ips'; + $method = 'POST'; + $data = array( + 'type' => $params['ip_type'] ?? 'ipv4', + 'description' => $params['description'] ?? 'Created via WHMCS', + 'labels' => $params['labels'] ?? array(), + 'home_location' => $params['location'] ?? null, + 'server' => isset($params['assign_to_server']) ? ArkHostHetznerVPS_GetVPSID($params) : null + ); + break; + + case 'Delete Floating IP': + $url .= 'floating_ips/' . $params['floating_ip_id']; + $method = 'DELETE'; + break; + + case 'Update Floating IP': + $url .= 'floating_ips/' . $params['floating_ip_id']; + $method = 'PUT'; + $data = array( + 'description' => $params['description'] ?? null, + 'labels' => $params['labels'] ?? null + ); + break; + + case 'Change Floating IP DNS': + $url .= 'floating_ips/' . $params['floating_ip_id'] . '/actions/change_dns_ptr'; + $method = 'POST'; + $data = array( + 'ip' => $params['ip'], + 'dns_ptr' => $params['dns_ptr'] + ); + break; + + case 'Server Actions': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions'; + $method = 'GET'; + break; + + case 'Change Protection': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/change_protection'; + $method = 'POST'; + $data = array( + 'delete' => isset($params['delete_protection']) ? $params['delete_protection'] : false, + 'rebuild' => isset($params['rebuild_protection']) ? $params['rebuild_protection'] : false + ); + break; + + case 'Rescue Mode': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/enable_rescue'; + $method = 'POST'; + $data = array( + 'type' => $params['rescue_type'] ?? 'linux64', + ); + break; + + case 'Disable Rescue Mode': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/disable_rescue'; + $method = 'POST'; + break; + + case 'Enable Backups': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/enable_backup'; + $method = 'POST'; + break; + + case 'Disable Backups': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/disable_backup'; + $method = 'POST'; + break; + + case 'List Volumes': + $url .= 'volumes?server=' . ArkHostHetznerVPS_GetVPSID($params); + $method = 'GET'; + break; + + case 'Create Volume': + $url .= 'volumes'; + $method = 'POST'; + $data = array( + 'size' => $params['size'] ?? 10, + 'name' => $params['name'] ?? 'volume-' . time(), + 'server' => ArkHostHetznerVPS_GetVPSID($params), + 'automount' => isset($params['automount']) ? $params['automount'] : true, + 'format' => $params['format'] ?? 'ext4' + ); + break; + + case 'Attach Volume': + $url .= 'volumes/' . $params['volume_id'] . '/actions/attach'; + $method = 'POST'; + $data = array( + 'server' => ArkHostHetznerVPS_GetVPSID($params), + 'automount' => isset($params['automount']) ? $params['automount'] : true + ); + break; + + case 'Detach Volume': + $url .= 'volumes/' . $params['volume_id'] . '/actions/detach'; + $method = 'POST'; + break; + + case 'Delete Volume': + $url .= 'volumes/' . $params['volume_id']; + $method = 'DELETE'; + break; + + case 'List Networks': + $url .= 'networks'; + $method = 'GET'; + break; + + case 'Attach to Network': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/attach_to_network'; + $method = 'POST'; + $data = array( + 'network' => $params['network_id'], + 'ip' => isset($params['ip']) ? $params['ip'] : null, + 'alias_ips' => isset($params['alias_ips']) ? $params['alias_ips'] : array() + ); + break; + + case 'Detach from Network': + $url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/detach_from_network'; + $method = 'POST'; + $data = array( + 'network' => $params['network_id'] + ); + break; + + default: + throw new Exception('Invalid action: ' . $params['action']); + break; + } + + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method); + curl_setopt($curl, CURLOPT_TIMEOUT, 15); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_301); + curl_setopt($curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + curl_setopt($curl, CURLOPT_USERAGENT, 'ArkHostHetznerVPS WHMCS'); + curl_setopt($curl, CURLOPT_HTTPHEADER, array( + 'Authorization: Bearer ' . $params['serverpassword'], + 'Content-Type: application/json' + )); + + if ($method === 'POST' || $method === 'PATCH' || $method === 'PUT') { + curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data)); + } + + $responseData = curl_exec($curl); + $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + + $responseData = json_decode($responseData, true); + + if ($statusCode === 0) throw new Exception('cURL Error: ' . curl_error($curl)); + + curl_close($curl); + + + logModuleCall( + 'ArkHostHetznerVPS', + $url, + !empty($data) ? json_encode($data) : '', + print_r($responseData, true) + ); + + // Hetzner API uses standard HTTP status codes + if ($statusCode >= 400) { + $errorMessage = 'Unknown error'; + $errorCode = ''; + + if (isset($responseData['error'])) { + $error = $responseData['error']; + $errorMessage = isset($error['message']) ? $error['message'] : $errorMessage; + $errorCode = isset($error['code']) ? ' (Code: ' . $error['code'] . ')' : ''; + + // Add details if available + if (isset($error['details'])) { + $details = is_array($error['details']) ? json_encode($error['details']) : $error['details']; + $errorMessage .= ' - Details: ' . $details; + } + } + + throw new Exception($errorMessage . $errorCode); + } + + return $responseData; +} + +function ArkHostHetznerVPS_Error($func, $params, Exception $err) { + logModuleCall('ArkHostHetznerVPS', $func, $params, $err->getMessage(), $err->getTraceAsString()); +} + +function ArkHostHetznerVPS_MetaData() { + return array( + 'DisplayName' => 'ArkHost - HetznerVPS', + 'APIVersion' => '1.1', + 'RequiresServer' => true, + ); +} + +function ArkHostHetznerVPS_ConfigOptions() { + $error = array( + 'error' => array( + 'FriendlyName' => 'Error', + 'Description' => 'Please double check if you selected a Server Group and/or your details are correct.', + 'Type' => '', + ), + ); + + $array = array( + 'planid' => array( + 'FriendlyName' => 'Server Type', + 'Description' => 'The Hetzner server type (Configurable option: planid).', + 'Type' => 'dropdown', + 'Options' => array(), + ), + 'osid' => array( + 'FriendlyName' => 'Operating System', + 'Description' => 'The Operating System image (Configurable option: osid).', + 'Type' => 'dropdown', + 'Options' => array(), + ), + 'datacenter' => array( + 'FriendlyName' => 'Datacenter', + 'Description' => 'The datacenter location for the server.', + 'Type' => 'dropdown', + 'Options' => array(), + ), + 'backups' => array( + 'FriendlyName' => 'Enable Backups', + 'Description' => 'Enable automatic backups (additional cost - billed by Hetzner, ~20% of server price).', + 'Type' => 'yesno', + ), + 'ipv6' => array( + 'FriendlyName' => 'Disable IPv6', + 'Description' => 'Disable IPv6 for this server.', + 'Type' => 'yesno', + ), + 'floating_ip_location' => array( + 'FriendlyName' => 'Floating IP Location', + 'Description' => 'Location for floating IPs (same as datacenter)', + 'Type' => 'dropdown', + 'Options' => array(), + ), + 'create_floating_ip' => array( + 'FriendlyName' => 'Create Floating IP', + 'Description' => 'Automatically create a floating IP when provisioning this server (additional cost - billed by Hetzner).', + 'Type' => 'yesno', + ), + ); + + try { + if (basename($_SERVER['SCRIPT_NAME'], '.php') === 'configproducts' && ($_REQUEST['action'] === 'module-settings' || $_POST['action'] === 'module-settings')) { + $id = 0; + $product = null; + $serverGroup = 0; + + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $id = (int) $_POST['id']; + + $product = Capsule::table('tblproducts')->where('id', $id)->first(); + $serverGroup = (int) $_POST['servergroup']; + } else { + $id = (int) $_REQUEST['id']; + + $product = Capsule::table('tblproducts')->where('id', $id)->first(); + $serverGroup = (int) $product->servergroup; + } + + // Only proceed if a server group is actually selected + if ($serverGroup == 0) { + return $array; // Return basic array structure when no server group selected + } + + $serverGroup = Capsule::table('tblservergroupsrel')->where('groupid', $serverGroup)->first(); + if (!$serverGroup) { + // Return basic array if server group not found, don't throw exception + return $array; + } + + $server = Capsule::table('tblservers')->where('id', $serverGroup->serverid)->first(); + if (!$server) { + // Return basic array if server not found, don't throw exception + return $array; + } + + $params = array( + 'serverusername' => $server->username, + 'serverpassword' => decrypt($server->password), + ); + + $params['action'] = 'Packages'; + $packageslist = ArkHostHetznerVPS_API($params); + + // Hetzner returns server types in 'server_types' array + if (isset($packageslist['server_types'])) { + foreach ($packageslist['server_types'] as $package) { + $price = isset($package['prices'][0]['price_monthly']['gross']) ? + number_format($package['prices'][0]['price_monthly']['gross'], 2) : '0.00'; + $array['planid']['Options'] += array( + $package['name'] => $package['description'] . ' (€' . $price . '/mo)' + ); + } + } + + if ($product->configoption1 == '') return $array; + + $params['action'] = 'Operating Systems'; + $params['plan_id'] = $product->configoption1; + $operatingSystems = ArkHostHetznerVPS_API($params); + + // Hetzner returns images in 'images' array + if (isset($operatingSystems['images'])) { + foreach ($operatingSystems['images'] as $operatingSystem) { + if ($operatingSystem['type'] === 'system' && $operatingSystem['status'] === 'available') { + $array['osid']['Options'] += array( + $operatingSystem['name'] => $operatingSystem['description'] ?: $operatingSystem['name'] + ); + } + } + } + + // Fetch datacenters + $params['action'] = 'Datacenters'; + $datacenters = ArkHostHetznerVPS_API($params); + + if (isset($datacenters['datacenters'])) { + foreach ($datacenters['datacenters'] as $datacenter) { + $array['datacenter']['Options'] += array( + $datacenter['name'] => $datacenter['description'] . ' (' . $datacenter['location']['city'] . ')' + ); + // Also populate floating IP locations + $array['floating_ip_location']['Options'] += array( + $datacenter['location']['name'] => $datacenter['location']['city'] . ', ' . $datacenter['location']['country'] + ); + } + } + } + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + + // Return the basic array structure with error message instead of error array + // This prevents the fields from disappearing + return $array; + } + + return $array; +} + +function ArkHostHetznerVPS_GetOption(array $params, $id, $default = NULL) { + $options = ArkHostHetznerVPS_ConfigOptions(); + + $friendlyName = $options[$id]['FriendlyName']; + + if (isset($params['configoptions'][$friendlyName]) && $params['configoptions'][$friendlyName] !== '') { + return $params['configoptions'][$friendlyName]; + } else if (isset($params['configoptions'][$id]) && $params['configoptions'][$id] !== '') { + return $params['configoptions'][$id]; + } else if (isset($params['customfields'][$friendlyName]) && $params['customfields'][$friendlyName] !== '') { + return $params['customfields'][$friendlyName]; + } else if (isset($params['customfields'][$id]) && $params['customfields'][$id] !== '') { + return $params['customfields'][$id]; + } + + $found = false; + $i = 0; + + foreach ($options as $key => $value) { + $i++; + if ($key === $id) { + $found = true; + break; + } + } + + if ($found && isset($params['configoption' . $i]) && $params['configoption' . $i] !== '') { + return $params['configoption' . $i]; + } + + return $default; +} + +function ArkHostHetznerVPS_TestConnection(array $params) { + $err = ''; + + try { + $params['action'] = 'Test'; + ArkHostHetznerVPS_API($params); + } catch (Exception $e) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $e); + $err = 'Received the error: ' . $e->getMessage() . ' Check module debug log for more detailed error.'; + } + + return [ + 'success' => $err === '', + 'error' => $err, + ]; +} + +function ArkHostHetznerVPS_CreateAccount(array $params) { + try { + $params['action'] = 'Order'; + $create = ArkHostHetznerVPS_API($params); + + // Validate Hetzner API response + if (!is_array($create) || !isset($create['server']['id'])) { + if (is_array($create)) { + } + throw new Exception('Invalid response from API'); + } + + // Store the server ID (using our format) + $params['model']->serviceProperties->save([ + 'ArkHostHetznerVPS|VPS ID' => $create['server']['id'], + ]); + + // Store the root password if provided + if (isset($create['root_password'])) { + Capsule::table('tblhosting')->where('id', $params['serviceid'])->update([ + 'password' => encrypt($create['root_password']) + ]); + } + + // Handle floating IP creation if requested via Configurable Options or Module Settings + $floatingIpType = ArkHostHetznerVPS_GetConfigurableOption($params, 'floating'); + $createFloatingIP = ArkHostHetznerVPS_GetOption($params, 'create_floating_ip'); + + if (($floatingIpType && $floatingIpType !== '' && strtolower($floatingIpType) !== 'none') || + ($createFloatingIP && $createFloatingIP === 'on')) { + + // Determine floating IP type - default to ipv4 if module setting is used + $ipType = $floatingIpType && $floatingIpType !== '' ? $floatingIpType : 'ipv4'; + try { + $floatingIpParams = $params; + $floatingIpParams['action'] = 'Create Floating IP'; + $floatingIpParams['ip_type'] = $ipType; + $floatingIpParams['location'] = ArkHostHetznerVPS_GetOption($params, 'floating_ip_location'); + $floatingIpParams['description'] = 'Server ID: ' . $create['server']['id']; + $floatingIpParams['assign_to_server'] = true; + + $floatingIp = ArkHostHetznerVPS_API($floatingIpParams); + + if (isset($floatingIp['floating_ip']['id'])) { + + // Store floating IP information + $params['model']->serviceProperties->save([ + 'ArkHostHetznerVPS|Floating IP ID' => $floatingIp['floating_ip']['id'], + 'ArkHostHetznerVPS|Floating IP' => $floatingIp['floating_ip']['ip'], + ]); + } + } catch (Exception $floatingIpErr) { + // You might want to send an admin notification here + } + } + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + return 'Received the error: ' . $err->getMessage() . ' Check module debug log for more detailed error.'; + } + + return 'success'; +} + +function ArkHostHetznerVPS_SuspendAccount(array $params) { + try { + $params['action'] = 'Disable'; + ArkHostHetznerVPS_API($params); + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + return 'Received the error: ' . $err->getMessage() . ' Check module debug log for more detailed error.'; + } + + return 'success'; +} + +function ArkHostHetznerVPS_UnsuspendAccount(array $params) { + try { + $params['action'] = 'Enable'; + ArkHostHetznerVPS_API($params); + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + return 'Received the error: ' . $err->getMessage() . ' Check module debug log for more detailed error.'; + } + + return 'success'; +} + +function ArkHostHetznerVPS_TerminateAccount(array $params) { + try { + // First, check if there's a floating IP to delete + $floatingIpId = $params['model']->serviceProperties->get('ArkHostHetznerVPS|Floating IP ID'); + if ($floatingIpId) { + try { + $floatingIpParams = $params; + $floatingIpParams['action'] = 'Delete Floating IP'; + $floatingIpParams['floating_ip_id'] = $floatingIpId; + ArkHostHetznerVPS_API($floatingIpParams); + + } catch (Exception $floatingIpErr) { + } + } + + // Then terminate the server + $params['action'] = 'Cancel'; + $params['when'] = 'now'; + ArkHostHetznerVPS_API($params); + + Capsule::table('tblhosting')->where('id', $params['serviceid'])->update(array( + 'username' => '', + 'password' => '', + )); + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + return 'Received the error: ' . $err->getMessage() . ' Check module debug log for more detailed error.'; + } + + return 'success'; +} + + + +function ArkHostHetznerVPS_ChangePackage(array $params) { + try { + $params['action'] = 'Upgrade'; + ArkHostHetznerVPS_API($params); + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + return 'Received the error: ' . $err->getMessage() . ' Check module debug log for more detailed error.'; + } + + return 'success'; +} + +function ArkHostHetznerVPS_Start(array $params) { + try { + $params['action'] = 'Start'; + ArkHostHetznerVPS_API($params); + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + return 'Received the error: ' . $err->getMessage() . ' Check module debug log for more detailed error.'; + } + + return 'success'; +} + +function ArkHostHetznerVPS_Reboot(array $params) { + try { + $params['action'] = 'Reboot'; + ArkHostHetznerVPS_API($params); + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + return 'Received the error: ' . $err->getMessage() . ' Check module debug log for more detailed error.'; + } + + return 'success'; +} + +function ArkHostHetznerVPS_Stop(array $params) { + try { + $params['action'] = 'Stop'; + ArkHostHetznerVPS_API($params); + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + return 'Received the error: ' . $err->getMessage() . ' Check module debug log for more detailed error.'; + } + + return 'success'; +} + +function ArkHostHetznerVPS_VNC(array $params) { + try { + $params['action'] = 'VNC Console'; + $vnc = ArkHostHetznerVPS_API($params); + + // Hetzner returns wss_url and password + if (isset($vnc['wss_url']) && isset($vnc['password'])) { + $consoleUrl = $vnc['wss_url']; + $vncPassword = $vnc['password']; + + // Generate a standalone HTML file that can be opened separately + $html = ' + + + VNC Console - ' . htmlspecialchars($params['domain']) . ' + + + + + +
+ VNC Console - ' . htmlspecialchars($params['domain']) . ' +
+ Password: ' . htmlspecialchars($vncPassword) . ' +
+
+ +
Initializing VNC Client...
+
+ + + +'; + + // Set proper headers to prevent session issues + header('Content-Type: text/html; charset=utf-8'); + header('Cache-Control: no-cache, no-store, must-revalidate'); + header('Pragma: no-cache'); + header('Expires: 0'); + + // Output the HTML and exit immediately + echo $html; + die(); + + } else { + throw new Exception('Console URL not found in response'); + } + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + + header('Content-Type: text/html; charset=utf-8'); + echo ' + + + VNC Console Error + + + +
+

VNC Console Error

+
' . htmlspecialchars($err->getMessage()) . '
+

Unable to open VNC console. Please try again or contact support if the problem persists.

+ Close Window +
+ +'; + die(); + } +} + +function ArkHostHetznerVPS_AdminCustomButtonArray() { + return array( + 'Start' => 'Start', + 'Stop'=> 'Stop', + 'Reboot' => 'Reboot', + 'Shutdown' => 'Shutdown', + 'VNC Console'=> 'VNC', + 'Enable Rescue' => 'EnableRescue', + 'Reset Root Password' => 'ResetRoot', + 'Create Snapshot' => 'CreateSnapshot', + 'Enable Backups' => 'EnableBackups', + 'Disable Backups' => 'DisableBackups', + ); +} + +function ArkHostHetznerVPS_AdminLink(array $params) { + try { + // Check if we have a VPS ID first + $vpsId = ArkHostHetznerVPS_GetVPSID($params); + if (!$vpsId) { + // This might be called from server configuration page where there's no service + return ' Hetzner Cloud Server'; + } + + // Get server info to display + $params['action'] = 'Server Info'; + $serverInfo = ArkHostHetznerVPS_API($params); + + if (isset($serverInfo['server'])) { + $server = $serverInfo['server']; + return ' Status: ' . $server['status'] . '
IP: ' . $server['public_net']['ipv4']['ip']; + } + + return 'Server ID: ' . $vpsId; + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + return 'Unable to retrieve server info'; + } +} + +function ArkHostHetznerVPS_ClientAreaAPI(array $params) { + try { + $action = App::getFromRequest('api'); + + $actions = array('Server Info', 'Graphs', 'Reinstall', 'Reboot', 'Stop', 'Start', 'IPv4 Addresses', 'Hostname rDNS', 'Create backup', 'Delete backup', 'List backups', 'Restore backup', 'Get Firewall rules', 'Add Firewall rules', 'Delete Firewall rule', 'Commit Firewall rules', 'ISO Images', 'Load ISO', 'Eject ISO', 'Reset root', 'Create Snapshot', 'List Snapshots', 'Server Metrics', 'Rescue Mode', 'Disable Rescue Mode', 'GetFloatingIPStatus', 'AssignFloatingIP', 'UnassignFloatingIP', 'SetFloatingIPReverseDNS'); + $results = array('result' => 'success'); + + if (in_array($action, $actions)) { + foreach ($_POST as $key => $value) { + $params[$key] = $value; + } + + // Check backup permissions + $backupActions = array('Create backup', 'Delete backup', 'Restore backup'); + if (in_array($action, $backupActions)) { + // Check if backups are enabled either via module settings or configurable options + $backupsEnabled = false; + + // Check module product settings + if (isset($params['backups']) && $params['backups'] == 'on') { + $backupsEnabled = true; + } + + // Check configurable options + if (isset($params['configoptions']['Backups']) && $params['configoptions']['Backups'] == 'Yes') { + $backupsEnabled = true; + } + + if (!$backupsEnabled) { + return array( + 'result' => 'error', + 'message' => 'Backups are not enabled for this service. Please upgrade your plan to enable backups.' + ); + } + } + + // Check floating IP permissions + $floatingIPActions = array('GetFloatingIPStatus', 'AssignFloatingIP', 'UnassignFloatingIP', 'SetFloatingIPReverseDNS'); + if (in_array($action, $floatingIPActions)) { + // Check if customer has floating IP via configurable options, module settings, or existing service + $floatingIPOption = ArkHostHetznerVPS_GetConfigurableOption($params, 'Floating IP'); + $moduleFloatingIP = ArkHostHetznerVPS_GetOption($params, 'create_floating_ip'); + $hasFloatingIPService = $params['model']->serviceProperties->get('ArkHostHetznerVPS|Floating IP ID'); + + if (!$floatingIPOption && !($moduleFloatingIP && $moduleFloatingIP === 'on') && !$hasFloatingIPService) { + return array('jsonResponse' => array( + 'result' => 'error', + 'message' => 'Floating IP not available for this service. Please upgrade your plan to add floating IP.' + )); + } + + // Handle GetFloatingIPStatus specially (not a real Hetzner API call) + if ($action === 'GetFloatingIPStatus') { + try { + // Get current server ID safely + $currentServerId = null; + try { + $currentServerId = ArkHostHetznerVPS_GetVPSID($params); + } catch (Exception $vpsIdErr) { + // VPS ID retrieval failed - continue without it + } + + // First, check if this service has a specific floating IP stored + $serviceFloatingIPId = $params['model']->serviceProperties->get('ArkHostHetznerVPS|Floating IP ID'); + $customerFloatingIP = null; + + if ($serviceFloatingIPId) { + // Get the specific floating IP for this service + try { + $floatingIPResult = ArkHostHetznerVPS_API(array_merge($params, array( + 'action' => 'Get Floating IP', + 'floating_ip_id' => $serviceFloatingIPId + ))); + if (isset($floatingIPResult['floating_ip'])) { + $customerFloatingIP = $floatingIPResult['floating_ip']; + } + } catch (Exception $e) { + // Floating IP might have been deleted outside WHMCS + // Remove invalid ID from service properties + $params['model']->serviceProperties->save([ + 'ArkHostHetznerVPS|Floating IP ID' => '', + 'ArkHostHetznerVPS|Floating IP' => '' + ]); + } + } + + // If no floating IP exists but customer has floating IP access, create one + if (!$customerFloatingIP && ($floatingIPOption || ($moduleFloatingIP && $moduleFloatingIP === 'on'))) { + try { + $createParams = $params; + $createParams['action'] = 'Create Floating IP'; + $createParams['ip_type'] = 'ipv4'; + $createParams['location'] = ArkHostHetznerVPS_GetOption($params, 'floating_ip_location') ?: + ArkHostHetznerVPS_GetOption($params, 'datacenter'); + $createParams['description'] = 'WHMCS Service ID: ' . $params['serviceid']; + $createParams['assign_to_server'] = true; + + $createResult = ArkHostHetznerVPS_API($createParams); + + if (isset($createResult['floating_ip']['id'])) { + // Store floating IP information in service properties + $params['model']->serviceProperties->save([ + 'ArkHostHetznerVPS|Floating IP ID' => $createResult['floating_ip']['id'], + 'ArkHostHetznerVPS|Floating IP' => $createResult['floating_ip']['ip'], + ]); + + $customerFloatingIP = $createResult['floating_ip']; + } + } catch (Exception $createErr) { + // Failed to create floating IP - log error but continue + logModuleCall( + 'ArkHostHetznerVPS', + 'CreateFloatingIPOnDemand', + $params, + 'Failed to create floating IP: ' . $createErr->getMessage(), + '', + array() + ); + } + } + + if ($customerFloatingIP) { + $floatingIPData = array( + 'hasFloatingIP' => true, + 'floatingIP' => $customerFloatingIP, + 'assigned' => $customerFloatingIP['server'] !== null, + 'server_id' => $customerFloatingIP['server'] ? $customerFloatingIP['server']['id'] : null, + 'current_server_id' => $currentServerId + ); + } else { + // Customer has floating IP access but no floating IP found - this is valid + $floatingIPData = array( + 'hasFloatingIP' => true, + 'floatingIP' => null, + 'assigned' => false, + 'server_id' => null, + 'current_server_id' => $currentServerId + ); + } + + return array('jsonResponse' => array( + 'result' => 'success', + 'data' => $floatingIPData + )); + } catch (Exception $statusErr) { + return array('jsonResponse' => array( + 'result' => 'error', + 'message' => 'Failed to load floating IP status: ' . $statusErr->getMessage() + )); + } + } + } + + $params['action'] = $action; + $result = ArkHostHetznerVPS_API($params); + + // Special handling for specific responses + if ($action === 'Graphs') { + // Handle Hetzner metrics response + if (isset($result['metrics'])) { + // Process time series data + $processedMetrics = array(); + + // Determine date format based on time period + $dateFormat = 'H:i'; // Default for hour/day + if (isset($params['time'])) { + switch ($params['time']) { + case 'hour': + $dateFormat = 'H:i'; + break; + case 'day': + $dateFormat = 'H:i'; + break; + case 'week': + $dateFormat = 'M d'; + break; + case 'month': + $dateFormat = 'M d'; + break; + case 'year': + $dateFormat = 'M Y'; + break; + } + } + + // Extract CPU usage + if (isset($result['metrics']['time_series']['cpu'])) { + $cpuData = $result['metrics']['time_series']['cpu']['values']; + $processedMetrics['cpu'] = array( + 'labels' => array(), + 'data' => array() + ); + foreach ($cpuData as $point) { + $processedMetrics['cpu']['labels'][] = date($dateFormat, $point[0]); + $processedMetrics['cpu']['data'][] = round($point[1], 2); + } + } + + // Extract disk I/O + if (isset($result['metrics']['time_series']['disk.0.iops.read'])) { + $diskReadData = $result['metrics']['time_series']['disk.0.iops.read']['values']; + $diskWriteData = $result['metrics']['time_series']['disk.0.iops.write']['values']; + $processedMetrics['disk'] = array( + 'labels' => array(), + 'read' => array(), + 'write' => array() + ); + foreach ($diskReadData as $i => $point) { + $processedMetrics['disk']['labels'][] = date($dateFormat, $point[0]); + $processedMetrics['disk']['read'][] = round($point[1], 2); + $processedMetrics['disk']['write'][] = isset($diskWriteData[$i]) ? round($diskWriteData[$i][1], 2) : 0; + } + } + + // Extract network traffic + if (isset($result['metrics']['time_series']['network.0.bandwidth.in'])) { + $netInData = $result['metrics']['time_series']['network.0.bandwidth.in']['values']; + $netOutData = $result['metrics']['time_series']['network.0.bandwidth.out']['values']; + $processedMetrics['network'] = array( + 'labels' => array(), + 'in' => array(), + 'out' => array() + ); + foreach ($netInData as $i => $point) { + $processedMetrics['network']['labels'][] = date($dateFormat, $point[0]); + // Convert bytes/s to Mbps + $processedMetrics['network']['in'][] = round(($point[1] * 8) / 1000000, 2); + $processedMetrics['network']['out'][] = isset($netOutData[$i]) ? round(($netOutData[$i][1] * 8) / 1000000, 2) : 0; + } + } + + $results['graphs'] = array( + 'type' => 'metrics', + 'data' => $processedMetrics + ); + } else { + // No metrics available + $results['graphs'] = array( + 'type' => 'none', + 'message' => 'Metrics not available for this server' + ); + } + } else if ($action === 'List backups' || $action === 'List Snapshots') { + // Handle image list response for Hetzner + if (isset($result['images'])) { + // Log the number of images found + logModuleCall( + 'ArkHostHetznerVPS', + 'ListBackups', + array('count' => count($result['images'])), + 'Found ' . count($result['images']) . ' backup images', + '', + array() + ); + + // Return backups with numeric keys for JavaScript compatibility + $backupIndex = 0; + foreach ($result['images'] as $image) { + $results[$backupIndex] = array( + 'id' => $image['id'], + 'name' => $image['description'] ?? $image['name'], + 'created' => $image['created'], + 'size' => isset($image['image_size']) ? round($image['image_size'], 2) : 0, // Already in GB from API + 'type' => $image['type'] + ); + $backupIndex++; + } + } else { + // No backups found + $results['message'] = 'No backups found'; + logModuleCall( + 'ArkHostHetznerVPS', + 'ListBackups', + array('result' => $result), + 'No images key in API response', + '', + array() + ); + } + } else if ($action === 'Server Info') { + // Handle server info for Hetzner + if (isset($result['server'])) { + $results = array_merge($results, $result['server']); + } + } else if ($action === 'ISO Images') { + // Handle ISO list for Hetzner + if (isset($result['isos'])) { + $results['isos'] = $result['isos']; + } + } else if ($action === 'Get Firewall rules') { + // For Hetzner, we need to check if server has firewalls attached + // and then fetch the firewall details separately + $rules = array(); + + // Check for firewall IDs in the correct location + $firewallIds = array(); + if (isset($result['server']['public_net']['firewalls']) && !empty($result['server']['public_net']['firewalls'])) { + // Extract firewall IDs from the firewalls array + foreach ($result['server']['public_net']['firewalls'] as $firewall) { + if (isset($firewall['id'])) { + $firewallIds[] = $firewall['id']; + } + } + } + + if (!empty($firewallIds)) { + // Server has firewalls attached, fetch the actual firewall rules + foreach ($firewallIds as $firewallId) { + // Fetch firewall details + $firewallParams = $params; + $firewallParams['action'] = 'Get Firewall Details'; + $firewallParams['firewall_id'] = $firewallId; + + try { + $firewallResult = ArkHostHetznerVPS_API($firewallParams); + + if (isset($firewallResult['firewall']['rules'])) { + foreach ($firewallResult['firewall']['rules'] as $rule) { + // Process inbound rules + if ($rule['direction'] === 'in') { + // Handle source IPs + $sourceIps = '0.0.0.0/0'; // Default when no source IPs specified + if (isset($rule['source_ips']) && is_array($rule['source_ips'])) { + if (!empty($rule['source_ips'])) { + // Join multiple source IPs with comma + $sourceIps = implode(', ', $rule['source_ips']); + } + // If source_ips is empty array, it means all sources are allowed + } + + // Handle port range or single port + $port = ''; + if (isset($rule['port'])) { + $port = $rule['port']; + } elseif (isset($rule['port_range'])) { + $port = $rule['port_range']; + } else { + $port = 'Any'; + } + + // Get description or use firewall name + $description = ''; + if (isset($rule['description']) && !empty($rule['description'])) { + $description = $rule['description']; + } else { + $description = 'Firewall: ' . (isset($firewallResult['firewall']['name']) ? $firewallResult['firewall']['name'] : $firewallId); + } + + $rules[] = array( + 'id' => 'fw_' . $firewallId . '_' . count($rules), + 'action' => 'ACCEPT', + 'protocol' => strtoupper($rule['protocol']), + 'port' => $port, + 'source' => $sourceIps, + 'note' => $description + ); + } + } + } + } catch (Exception $e) { + // If we can't fetch firewall details, show a message + $rules[] = array( + 'id' => 'error_' . $firewallId, + 'action' => 'INFO', + 'protocol' => 'N/A', + 'port' => 'N/A', + 'source' => 'N/A', + 'note' => 'Unable to fetch firewall ' . $firewallId . ' details' + ); + } + } + + if (empty($rules)) { + $results['message'] = 'This server has ' . count($result['server']['firewall_ids']) . ' firewall(s) attached but no inbound rules configured.'; + } + } else { + // No firewalls attached - show default rules indicating all traffic is allowed + $rules[] = array( + 'id' => '1', + 'action' => 'ACCEPT', + 'protocol' => 'TCP', + 'port' => '22', + 'source' => '0.0.0.0/0', + 'note' => 'SSH (Default Open)' + ); + $rules[] = array( + 'id' => '2', + 'action' => 'ACCEPT', + 'protocol' => 'TCP', + 'port' => '80', + 'source' => '0.0.0.0/0', + 'note' => 'HTTP (Default Open)' + ); + $rules[] = array( + 'id' => '3', + 'action' => 'ACCEPT', + 'protocol' => 'TCP', + 'port' => '443', + 'source' => '0.0.0.0/0', + 'note' => 'HTTPS (Default Open)' + ); + $rules[] = array( + 'id' => '4', + 'action' => 'ACCEPT', + 'protocol' => 'TCP/UDP', + 'port' => 'All', + 'source' => '0.0.0.0/0', + 'note' => 'All other ports (Default Open)' + ); + $rules[] = array( + 'id' => '5', + 'action' => 'ACCEPT', + 'protocol' => 'ICMP', + 'port' => 'N/A', + 'source' => '0.0.0.0/0', + 'note' => 'Ping/ICMP (Default Open)' + ); + + $results['message'] = 'No firewall attached. All traffic is allowed by default.'; + } + + // Return rules with numeric keys for JavaScript compatibility + foreach ($rules as $index => $rule) { + $results[$index] = $rule; + } + } else if ($action === 'Reinstall') { + // Handle rebuild response - save new root password if provided + if (isset($result['root_password']) && !empty($result['root_password'])) { + // Save the new root password + Capsule::table('tblhosting') + ->where('id', $params['serviceid']) + ->update(['password' => encrypt($result['root_password'])]); + + $results['root_password'] = $result['root_password']; + $results['message'] = 'Server rebuild initiated. New root password has been saved.'; + } else { + $results['message'] = 'Server rebuild initiated. No new password generated (SSH keys used).'; + } + $results = array_merge($results, is_array($result) ? $result : array('data' => $result)); + } else if ($action === 'AssignFloatingIP') { + // Handle floating IP assignment - only allow assignment of service's own floating IP + $serviceFloatingIPId = $params['model']->serviceProperties->get('ArkHostHetznerVPS|Floating IP ID'); + if ($serviceFloatingIPId && $serviceFloatingIPId === $params['floating_ip_id']) { + $assignResult = ArkHostHetznerVPS_API(array_merge($params, array('action' => 'Assign Floating IP'))); + $results = array_merge($results, is_array($assignResult) ? $assignResult : array('data' => $assignResult)); + } else { + $results['result'] = 'error'; + $results['message'] = 'Invalid floating IP ID for this service'; + } + } else if ($action === 'UnassignFloatingIP') { + // Handle floating IP unassignment - only allow unassignment of service's own floating IP + $serviceFloatingIPId = $params['model']->serviceProperties->get('ArkHostHetznerVPS|Floating IP ID'); + if ($serviceFloatingIPId && $serviceFloatingIPId === $params['floating_ip_id']) { + $unassignResult = ArkHostHetznerVPS_API(array_merge($params, array('action' => 'Unassign Floating IP'))); + $results = array_merge($results, is_array($unassignResult) ? $unassignResult : array('data' => $unassignResult)); + } else { + $results['result'] = 'error'; + $results['message'] = 'Invalid floating IP ID for this service'; + } + } else if ($action === 'SetFloatingIPReverseDNS') { + // Handle floating IP reverse DNS update - only allow for service's own floating IP + $serviceFloatingIPId = $params['model']->serviceProperties->get('ArkHostHetznerVPS|Floating IP ID'); + if ($serviceFloatingIPId && $serviceFloatingIPId === $params['floating_ip_id']) { + $dnsResult = ArkHostHetznerVPS_API(array_merge($params, array('action' => 'Change Floating IP DNS'))); + $results = array_merge($results, is_array($dnsResult) ? $dnsResult : array('data' => $dnsResult)); + } else { + $results['result'] = 'error'; + $results['message'] = 'Invalid floating IP ID for this service'; + } + } else { + $results = array_merge($results, is_array($result) ? $result : array('data' => $result)); + } + + return array('jsonResponse' => $results); + } else { + throw new Exception('Action not allowed'); + } + } catch (Exception $e) { + return array('jsonResponse' => array('result' => 'error', 'message' => $e->getMessage())); + } +} + +function ArkHostHetznerVPS_DeliverFile(array $params) { + try { + $dir = __DIR__ . '/template/'; + $file = App::getFromRequest('file'); + $files = array('app.min.css', 'app.min.js'); + + if (in_array($file, $files)) { + $type = ''; + + if (function_exists('ob_gzhandler')) { + ob_start('ob_gzhandler'); + } + + if (strpos($file, '.js') !== false) { + $dir .= 'js/'; + $type = 'application/javascript'; + } else if (strpos($file, '.css') !== false) { + $dir .= 'css/'; + $type = 'text/css'; + } else { + $type = 'text/html'; + } + + header('Content-Type: ' . $type . '; charset=utf-8'); + header('Cache-Control: max-age=604800, public'); + + echo file_get_contents($dir . $file); + WHMCS\Terminus::getInstance()->doExit(); + } else { + throw new Exception('File not found'); + } + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + return array('jsonResponse' => array('result' => 'error', 'message' => $err->getMessage())); + } +} + + + +function ArkHostHetznerVPS_ClientAreaCustomButtonArray() { + $_LANG = ArkHostHetznerVPS_Translation(); + + return array( + $_LANG['Start'] => 'Start', + $_LANG['Stop'] => 'Stop', + $_LANG['Restart'] => 'Reboot', + $_LANG['VNC'] => 'VNC', + ); +} + +function ArkHostHetznerVPS_ClientAreaAllowedFunctions() { + return array('ClientAreaAPI', 'DeliverFile'); +} + +function ArkHostHetznerVPS_ClientArea(array $params) { + if ($params['moduletype'] !== 'ArkHostHetznerVPS') return; + + try { + // Get clean VPS ID - this handles migration from other modules + $cleanVpsId = ArkHostHetznerVPS_GetVPSID($params); + if (empty($cleanVpsId)) { + throw new Exception('VPS ID not found. Please check service configuration.'); + } + + // If we found a VPS ID but it's not stored in our format, store it now for future use + $storedVpsId = $params['model']->serviceProperties->get('ArkHostHetznerVPS|VPS ID'); + if (empty($storedVpsId) && !empty($cleanVpsId)) { + // Store the VPS ID for future use (using our format) + $params['model']->serviceProperties->save([ + 'ArkHostHetznerVPS|VPS ID' => $cleanVpsId, + ]); + } + + + // Check if password is empty (migration from other modules) and set a dummy password + if (empty($params['password'])) { + // Set a dummy password to prevent WHMCS from showing password reset form + // This is just for display - actual server access is via API + Capsule::table('tblhosting') + ->where('id', $params['serviceid']) + ->update(['password' => encrypt('managed-via-api')]); + + // Reload params to get the updated password + $params['password'] = 'managed-via-api'; + } + + // Get server info and operating systems data + $params['action'] = 'Server Info'; + $response = ArkHostHetznerVPS_API($params); + + // Check if server info is valid + if (!is_array($response) || !isset($response['server'])) { + throw new Exception('Unable to retrieve server information from API'); + } + + // Extract server data from Hetzner response + $serverInfo = $response['server']; + + $params['action'] = 'Operating Systems - Server'; + $operatingSystemsTemp = ArkHostHetznerVPS_API($params); + + // Check if operating systems data is valid + if (!is_array($operatingSystemsTemp)) { + $operatingSystemsTemp = array(); + } + + $dirImages = __DIR__ . '/template/img/'; + $availableImages = glob($dirImages . '*.png'); + $images = array(); + + foreach ($availableImages as $key => $image) { + $images[explode('.png', explode($dirImages, $image)[1])[0]] = 'data:image/png;base64,' . base64_encode(file_get_contents($image)); + } + + $dirOS = __DIR__ . '/template/img/os/'; + $availableOS = glob($dirOS . '*.png'); + $operatingSystems = array(); + + foreach ($availableOS as $key => $os) { + $availableOS[$key] = explode('.png', explode($dirOS, $os)[1])[0]; + } + + // Process operating systems data from Hetzner API + if (!empty($operatingSystemsTemp) && isset($operatingSystemsTemp['images'])) { + // Log the raw OS data for debugging + logModuleCall( + 'ArkHostHetznerVPS', + 'ProcessOperatingSystems', + array('image_count' => count($operatingSystemsTemp['images'])), + 'Processing ' . count($operatingSystemsTemp['images']) . ' OS images', + '', + array() + ); + + foreach ($operatingSystemsTemp['images'] as $operatingSystem) { + // Skip non-system images + if ($operatingSystem['type'] !== 'system' || $operatingSystem['status'] !== 'available') { + continue; + } + + // Use description for display, fall back to name + $displayName = $operatingSystem['description'] ?: $operatingSystem['name']; + $osName = strtolower($displayName); + + // Determine the proper group based on OS name + if (strpos($osName, 'alma') !== false) { + $group = 'almalinux'; + $groupName = 'AlmaLinux'; + } elseif (strpos($osName, 'rocky') !== false) { + $group = 'rocky'; + $groupName = 'Rocky Linux'; + } elseif (strpos($osName, 'centos') !== false) { + $group = 'centos'; + $groupName = 'CentOS'; + } elseif (strpos($osName, 'debian') !== false) { + $group = 'debian'; + $groupName = 'Debian'; + } elseif (strpos($osName, 'ubuntu') !== false) { + $group = 'ubuntu'; + $groupName = 'Ubuntu'; + } elseif (strpos($osName, 'fedora') !== false) { + $group = 'fedora'; + $groupName = 'Fedora'; + } elseif (strpos($osName, 'opensuse') !== false || strpos($osName, 'suse') !== false) { + $group = 'opensuse'; + $groupName = 'openSUSE'; + } elseif (strpos($osName, 'windows') !== false) { + $group = 'windows'; + $groupName = 'Windows'; + } else { + // For any other OS, use 'others' + $group = 'others'; + $groupName = 'Other Systems'; + } + + if (!isset($operatingSystems[$group])) { + // Map group names to correct image filenames + $imageFile = $group; + if ($group === 'rocky') { + $imageFile = 'rockylinux'; + } + + $image = file_get_contents($dirOS . (in_array($imageFile, $availableOS) ? $imageFile : 'others') . '.png'); + + $operatingSystems[$group] = array( + 'name' => $groupName, + 'image' => 'data:image/png;base64,' . base64_encode($image), + 'versions' => array(), + ); + } + + // Check if this version already exists (avoid duplicates) + // Check both by ID and display name to avoid visual duplicates + $versionExists = false; + if (!empty($operatingSystems[$group]['versions'])) { + foreach ($operatingSystems[$group]['versions'] as $existingVersion) { + // Check if either the ID or display name already exists + if ($existingVersion['id'] === $operatingSystem['name'] || + $existingVersion['name'] === $displayName) { + $versionExists = true; + // Log duplicate found + logModuleCall( + 'ArkHostHetznerVPS', + 'DuplicateOSFound', + array( + 'group' => $group, + 'existing_id' => $existingVersion['id'], + 'existing_name' => $existingVersion['name'], + 'new_id' => $operatingSystem['name'], + 'new_name' => $displayName + ), + 'Duplicate OS version found', + '', + array() + ); + break; + } + } + } + + // Only add if not already present + if (!$versionExists) { + // Store both the API name (for rebuild) and display name + $operatingSystems[$group]['versions'][] = array( + 'id' => $operatingSystem['name'], // This is what we send to API + 'name' => $displayName // This is what we display + ); + } + } + + // Sort OS versions within each group for consistent display + foreach ($operatingSystems as $group => &$osData) { + if (!empty($osData['versions'])) { + usort($osData['versions'], function($a, $b) { + return strcmp($a['name'], $b['name']); + }); + } + } + + // Process operating system info for Hetzner + if (isset($serverInfo['image']) && !empty($operatingSystemsTemp['images'])) { + $osId = $serverInfo['image']['name']; + $osName = strtolower($serverInfo['image']['description'] ?? $osId); + + // Find matching OS in available images + $currentOS = null; + foreach ($operatingSystemsTemp['images'] as $os) { + if ($os['name'] === $osId) { + $currentOS = $os; + break; + } + } + + if ($currentOS) { + $displayName = $currentOS['description'] ?? $currentOS['name']; + $osName = strtolower($displayName); + + // Determine the proper group based on current OS name (same logic as above) + if (strpos($osName, 'alma') !== false) { + $group = 'almalinux'; + } elseif (strpos($osName, 'rocky') !== false) { + $group = 'rocky'; + } elseif (strpos($osName, 'centos') !== false) { + $group = 'centos'; + } elseif (strpos($osName, 'debian') !== false) { + $group = 'debian'; + } elseif (strpos($osName, 'ubuntu') !== false) { + $group = 'ubuntu'; + } elseif (strpos($osName, 'fedora') !== false) { + $group = 'fedora'; + } elseif (strpos($osName, 'opensuse') !== false || strpos($osName, 'suse') !== false) { + $group = 'opensuse'; + } elseif (strpos($osName, 'windows') !== false) { + $group = 'windows'; + } else { + $group = 'others'; + } + + // Map group names to correct image filenames + $imageFile = $group; + if ($group === 'rocky') { + $imageFile = 'rockylinux'; + } + + // Use the specific OS information + $serverInfo['operatingSystem'] = array( + 'name' => $displayName, + 'image' => isset($operatingSystems[$group]) ? $operatingSystems[$group]['image'] : 'data:image/png;base64,' . base64_encode(file_get_contents($dirOS . (in_array($imageFile, $availableOS) ? $imageFile : 'others') . '.png')) + ); + } else { + // Fallback if OS not found - use image info from server + $displayName = $serverInfo['image']['description'] ?? $osId; + $osName = strtolower($displayName); + + // Try to determine OS type from name + if (strpos($osName, 'alma') !== false) { + $imageFile = 'almalinux'; + } elseif (strpos($osName, 'rocky') !== false) { + $imageFile = 'rockylinux'; + } elseif (strpos($osName, 'centos') !== false) { + $imageFile = 'centos'; + } elseif (strpos($osName, 'debian') !== false) { + $imageFile = 'debian'; + } elseif (strpos($osName, 'ubuntu') !== false) { + $imageFile = 'ubuntu'; + } elseif (strpos($osName, 'fedora') !== false) { + $imageFile = 'fedora'; + } elseif (strpos($osName, 'opensuse') !== false || strpos($osName, 'suse') !== false) { + $imageFile = 'opensuse'; + } elseif (strpos($osName, 'windows') !== false) { + $imageFile = 'windows'; + } else { + $imageFile = 'others'; + } + + $serverInfo['operatingSystem'] = array( + 'name' => $displayName, + 'image' => 'data:image/png;base64,' . base64_encode(file_get_contents($dirOS . (in_array($imageFile, $availableOS) ? $imageFile : 'others') . '.png')) + ); + } + } + } + + // Set default OS info if not available + if (!isset($serverInfo['operatingSystem']) || !is_array($serverInfo['operatingSystem'])) { + $serverInfo['operatingSystem'] = array( + 'name' => 'Unknown OS', + 'image' => 'data:image/png;base64,' . base64_encode(file_get_contents($dirOS . 'others.png')) + ); + } + + // Map Hetzner status to template expectations + $serverInfo['statusImage'] = isset($images[$serverInfo['status']]) ? $images[$serverInfo['status']] : (isset($images['unknown']) ? $images['unknown'] : ''); + $serverInfo['statusDescription'] = ucfirst($serverInfo['status']); + + // Map Hetzner server data to expected format + $serverInfo['hostname'] = $serverInfo['name'] ?? 'N/A'; + $serverInfo['ip'] = isset($serverInfo['public_net']['ipv4']['ip']) ? $serverInfo['public_net']['ipv4']['ip'] : 'N/A'; + $serverInfo['ipv6'] = isset($serverInfo['public_net']['ipv6']['ip']) ? $serverInfo['public_net']['ipv6']['ip'] : 'N/A'; + + // Calculate uptime from created date + if (isset($serverInfo['created'])) { + $created = new DateTime($serverInfo['created']); + $now = new DateTime(); + $diff = $now->diff($created); + $serverInfo['uptime_text'] = $diff->format('%a days, %h hours'); + } else { + $serverInfo['uptime_text'] = 'N/A'; + } + + // Get server type details + $serverType = $serverInfo['server_type'] ?? array(); + $serverInfo['cpu'] = $serverType['cores'] ?? 0; + $serverInfo['ram'] = $serverType['memory'] ?? 0; + $serverInfo['disk'] = $serverType['disk'] ?? 0; + + // Try to fetch current metrics for the overview + try { + $metricsParams = $params; + $metricsParams['action'] = 'Graphs'; + $metricsParams['time'] = 'hour'; // Get last hour for current usage + + $metrics = ArkHostHetznerVPS_API($metricsParams); + + // Extract latest CPU usage + if (isset($metrics['metrics']['time_series']['cpu']['values'])) { + $cpuValues = $metrics['metrics']['time_series']['cpu']['values']; + if (!empty($cpuValues)) { + $latestCpu = end($cpuValues); + $serverInfo['cpu_usage'] = round($latestCpu[1], 1); + } + } + + // For bandwidth, we should use the outgoing_traffic from server info instead of calculating from metrics + // The metrics only show current bandwidth rate, not total usage + } catch (Exception $e) { + // If metrics fail, keep defaults + } + + // Hetzner doesn't provide RAM usage or disk usage via API + $serverInfo['cpu_usage'] = $serverInfo['cpu_usage'] ?? 0; + $serverInfo['ram_usage'] = 0; + $serverInfo['disk_used'] = 0; + + // Traffic information - Hetzner includes 20TB with all cloud servers + // Traffic usage is not available via the server API + $serverInfo['bandwidth'] = 20480; // 20TB in GB (20 * 1024) + $serverInfo['bandwidth_used'] = 0; // Not available via API + + // Add datacenter info + $serverInfo['datacenter'] = isset($serverInfo['datacenter']['description']) ? $serverInfo['datacenter']['description'] : 'N/A'; + $serverInfo['location'] = isset($serverInfo['datacenter']['location']['city']) ? $serverInfo['datacenter']['location']['city'] : 'N/A'; + + // For now, always show backups tab - the API will handle permissions + $backupsEnabled = true; + + return array( + 'templatefile' => 'template/clientarea_direct', + 'templateVariables' => array( + 'images' => $images, + 'serverInfo' => $serverInfo, + 'operatingSystems' => $operatingSystems, + 'token' => generate_token('plain'), + 'ADDONLANG' => ArkHostHetznerVPS_Translation(), + 'backupsEnabled' => $backupsEnabled, + ) + ); + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + + return array( + 'templatefile' => 'template/error', + 'templateVariables' => array( + 'error' => $err->getMessage(), + 'image' => 'data:image/png;base64,' . base64_encode(file_get_contents(__DIR__ . '/template/img/notice.png')) + ) + ); + } +} + +function ArkHostHetznerVPS_Translation() { + $lang = Setting::getValue('Language'); + $language = Lang::getName(); + $_ADDONLANG = []; + + if ($language === '') { + $language = $lang; + } + + if ($language) { + $addonLangFile = ROOTDIR . '/modules/servers/ArkHostHetznerVPS/lang/' . $language . '.php'; + + if (file_exists($addonLangFile)) { + swapLang($language); + + ob_start(); + require $addonLangFile; + ob_end_clean(); + } + } + + if (count($_ADDONLANG) === 0) { + $addonLangFile = ROOTDIR . '/modules/servers/ArkHostHetznerVPS/lang/' . $lang . '.php'; + + if (file_exists($addonLangFile)) { + swapLang($lang); + + ob_start(); + require $addonLangFile; + ob_end_clean(); + } + } + + if (count($_ADDONLANG) === 0) { + $addonLangFile = ROOTDIR . '/modules/servers/ArkHostHetznerVPS/lang/english.php'; + + if (file_exists($addonLangFile)) { + ob_start(); + require $addonLangFile; + ob_end_clean(); + } + } + + return $_ADDONLANG; +} + +function ArkHostHetznerVPS_EnableRescue(array $params) { + try { + $params['action'] = 'Rescue Mode'; + ArkHostHetznerVPS_API($params); + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + return 'Received the error: ' . $err->getMessage() . ' Check module debug log for more detailed error.'; + } + + return 'success'; +} + +function ArkHostHetznerVPS_ResetRoot(array $params) { + try { + $params['action'] = 'Reset root'; + $result = ArkHostHetznerVPS_API($params); + + // Store the new root password if provided + if (isset($result['root_password'])) { + Capsule::table('tblhosting')->where('id', $params['serviceid'])->update([ + 'password' => encrypt($result['root_password']) + ]); + } + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + return 'Received the error: ' . $err->getMessage() . ' Check module debug log for more detailed error.'; + } + + return 'success'; +} + +function ArkHostHetznerVPS_CreateSnapshot(array $params) { + try { + $params['action'] = 'Create Snapshot'; + $params['description'] = 'Manual snapshot from WHMCS'; + ArkHostHetznerVPS_API($params); + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + return 'Received the error: ' . $err->getMessage() . ' Check module debug log for more detailed error.'; + } + + return 'success'; +} + +function ArkHostHetznerVPS_Shutdown(array $params) { + try { + $params['action'] = 'Shutdown'; + ArkHostHetznerVPS_API($params); + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + return 'Received the error: ' . $err->getMessage() . ' Check module debug log for more detailed error.'; + } + + return 'success'; +} + +function ArkHostHetznerVPS_EnableBackups(array $params) { + try { + $params['action'] = 'Enable Backups'; + ArkHostHetznerVPS_API($params); + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + return 'Received the error: ' . $err->getMessage() . ' Check module debug log for more detailed error.'; + } + + return 'success'; +} + +function ArkHostHetznerVPS_DisableBackups(array $params) { + try { + $params['action'] = 'Disable Backups'; + ArkHostHetznerVPS_API($params); + } catch (Exception $err) { + ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err); + return 'Received the error: ' . $err->getMessage() . ' Check module debug log for more detailed error.'; + } + + return 'success'; +} diff --git a/modules/servers/ArkHostHetznerVPS/lang/dutch.php b/modules/servers/ArkHostHetznerVPS/lang/dutch.php new file mode 100644 index 0000000..2e4b29b --- /dev/null +++ b/modules/servers/ArkHostHetznerVPS/lang/dutch.php @@ -0,0 +1,231 @@ +** De geautomatiseerde back-ups vervangen ook uw handmatige back-ups, tenzij de geautomatiseerde back-ups zijn uitgeschakeld.
*** De geautomatiseerde back-ups worden 2 keer per week gemaakt en zijn onderdeel van ons disaster recovery plan. Als u de geautomatiseerde back-ups uitschakelt, schakelt u ook elke kans op herstel uit in geval van een ramp.
**** Het bestandssysteem van de back-up is mogelijk niet volledig consistent als de VPS aan het schrijven was naar het bestandssysteem op het moment van de back-up. Voor volledig consistente back-ups moet de server worden gestopt tijdens het maken van de back-up.'; +$_ADDONLANG['Backups']['Date'] = 'Datum'; +$_ADDONLANG['Backups']['Size'] = 'Grootte'; +$_ADDONLANG['Backups']['Type'] = 'Type'; +$_ADDONLANG['Backups']['Status'] = 'Status'; +$_ADDONLANG['Backups']['Actions'] = 'Acties'; +$_ADDONLANG['Backups']['Create'] = 'Nu back-up maken'; +$_ADDONLANG['Backups']['Available'] = 'Beschikbaar'; +$_ADDONLANG['Backups']['Creating'] = 'Bezig met maken...'; +$_ADDONLANG['Backups']['Error'] = 'Fout'; +$_ADDONLANG['Backups']['Automatic'] = 'Automatisch'; +$_ADDONLANG['Backups']['Manual'] = 'Handmatig'; + +## Settings +$_ADDONLANG['Settings']['Title'] = 'Instellingen Menu'; +### Hostname +$_ADDONLANG['Settings']['Hostname']['Title'] = 'Hostnaam'; +$_ADDONLANG['Settings']['Hostname']['Description'] = 'Stelt de hostnaam en het rDNS in. Maak eerst het A-record aan.'; +$_ADDONLANG['Settings']['Hostname']['Submit'] = 'Versturen'; + +### ISO +$_ADDONLANG['Settings']['ISO']['Title'] = 'ISO'; +$_ADDONLANG['Settings']['ISO']['Description'] = 'Als u het besturingssysteem via de ISO-image installeert, moet u ook de netwerkinterface statisch configureren. Er draait geen DHCP-server.'; +$_ADDONLANG['Settings']['ISO']['Image'] = 'ISO Image'; +$_ADDONLANG['Settings']['ISO']['Submit'] = 'ISO laden'; +$_ADDONLANG['Settings']['ISO']['Remove'] = 'ISO uitwerpen'; + +### Password +$_ADDONLANG['Settings']['Password']['Title'] = 'Wachtwoord'; +$_ADDONLANG['Settings']['Password']['Description'] = 'Het installatie wachtwoord wordt na 72 uur uit onze systemen verwijderd. Het is verplicht om het wachtwoord bij uw eerste aanmelding te wijzigen!'; +$_ADDONLANG['Settings']['Password']['Submit'] = 'Wachtwoord resetten'; + +### Reinstall +$_ADDONLANG['Settings']['Reinstall']['Title'] = 'Herinstallatie'; +$_ADDONLANG['Settings']['Reinstall']['Description'] = 'Begrijp alstublieft dat door herinstallatie alle gegevens van de server worden gewist. Deze actie is onomkeerbaar!'; +$_ADDONLANG['Settings']['Reinstall']['OS'] = 'Besturingssysteem'; +$_ADDONLANG['Settings']['Reinstall']['Version'] = 'KIES VERSIE'; +$_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Herinstalleren'; + +### Firewall +$_ADDONLANG['Settings']['Firewall']['Title'] = 'Firewall'; +$_ADDONLANG['Settings']['Firewall']['Description'] = 'De regels worden van boven naar beneden geëvalueerd. Standaard is alles toegestaan. De firewall is alleen beschikbaar op de publieke interface. Alleen het inkomende verkeer wordt gefilterd door de firewall.'; +$_ADDONLANG['Settings']['Firewall']['Action'] = 'Actie'; +$_ADDONLANG['Settings']['Firewall']['Port'] = 'Poort'; +$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocol'; +$_ADDONLANG['Settings']['Firewall']['Source'] = 'Bron'; +$_ADDONLANG['Settings']['Firewall']['Note'] = 'Opmerking'; +$_ADDONLANG['Settings']['Firewall']['Actions'] = 'Acties'; +$_ADDONLANG['Settings']['Firewall']['Accept'] = 'Accepteren'; +$_ADDONLANG['Settings']['Firewall']['Drop'] = 'Weggooien'; +$_ADDONLANG['Settings']['Firewall']['PortNumber'] = 'Poortnummer'; +$_ADDONLANG['Settings']['Firewall']['SourceLabel'] = 'Bijv.: x.x.x.x/xx (optioneel)'; +$_ADDONLANG['Settings']['Firewall']['Notes'] = 'Notities (optioneel)'; +$_ADDONLANG['Settings']['Firewall']['Warning'] = 'De regels moeten worden toegepast om effect te hebben.'; +$_ADDONLANG['Settings']['Firewall']['Submit'] = 'Firewall toepassen'; + +### Rescue Mode +$_ADDONLANG['Settings']['Rescue']['Title'] = 'Reddingsmodus'; +$_ADDONLANG['Settings']['Rescue']['Description'] = 'Reddingsmodus start uw server in een tijdelijk Linux-systeem waar u toegang heeft tot de schijven van uw server om gegevens te repareren of herstellen.'; +$_ADDONLANG['Settings']['Rescue']['Status'] = 'Status'; +$_ADDONLANG['Settings']['Rescue']['Active'] = 'Actief'; +$_ADDONLANG['Settings']['Rescue']['Inactive'] = 'Inactief'; +$_ADDONLANG['Settings']['Rescue']['Enable'] = 'Reddingsmodus inschakelen'; +$_ADDONLANG['Settings']['Rescue']['EnableReboot'] = 'Inschakelen en herstarten'; +$_ADDONLANG['Settings']['Rescue']['Disable'] = 'Reddingsmodus uitschakelen'; +$_ADDONLANG['Settings']['Rescue']['ResetRootPassword'] = 'Root wachtwoord resetten'; +$_ADDONLANG['Settings']['Rescue']['Warning'] = 'Na het inschakelen van de reddingsmodus moet u de server binnen 60 minuten herstarten om effect te hebben.'; +$_ADDONLANG['Settings']['Rescue']['PasswordNote'] = 'Het root wachtwoord wordt eenmaal weergegeven na het inschakelen van de reddingsmodus. Zorg ervoor dat u het opslaat!'; +$_ADDONLANG['Settings']['Rescue']['DisableTitle'] = 'Reddingsmodus uitschakelen'; +$_ADDONLANG['Settings']['Rescue']['DisableDescription'] = 'Als de reddingsmodus momenteel actief is, kunt u deze hier uitschakelen. De server zal normaal opstarten bij de volgende herstart.'; +$_ADDONLANG['Settings']['Rescue']['NewRootPassword'] = 'Nieuw Root Wachtwoord'; +$_ADDONLANG['Settings']['Rescue']['AboutTitle'] = 'Over Reddingsmodus'; +$_ADDONLANG['Settings']['Rescue']['AboutDescription'] = 'Het reddingssysteem is een netwerk-gebaseerde omgeving en kan worden gebruikt om problemen op te lossen die een reguliere opstarten verhinderen. Het is ook nuttig voor het installeren van aangepaste Linux-distributies die niet direct door ons worden aangeboden. U kunt de harde schijf van de server mounten binnen het reddingssysteem.'; +$_ADDONLANG['Settings']['Rescue']['Important'] = 'Belangrijk'; +$_ADDONLANG['Settings']['Rescue']['ImportantNote'] = 'Na het inschakelen van het reddingssysteem moet u de server in de komende 60 minuten herstarten om het te activeren. Na nog een herstart zal uw server weer opstarten vanaf de lokale schijf.'; +$_ADDONLANG['Settings']['Rescue']['EnableTitle'] = 'Reddingsmodus inschakelen'; +$_ADDONLANG['Settings']['Rescue']['EnableDescription'] = 'Opstarten in een minimaal Linux-systeem voor herstel- en onderhoudstaken.'; +$_ADDONLANG['Settings']['Rescue']['EnableRebootTitle'] = 'Inschakelen & Herstarten'; +$_ADDONLANG['Settings']['Rescue']['EnableRebootDescription'] = 'Reddingsmodus inschakelen en de server direct herstarten om het te activeren.'; +$_ADDONLANG['Settings']['Rescue']['PasswordPrompt'] = 'Uw nieuwe root wachtwoord is:'; + +# Additional strings for UI elements +$_ADDONLANG['General']['NoBackupsFound'] = 'Geen back-ups gevonden'; +$_ADDONLANG['General']['NoFirewallRules'] = 'Geen firewall regels geconfigureerd'; +$_ADDONLANG['General']['NoDataAvailable'] = 'Geen gegevens beschikbaar'; +$_ADDONLANG['General']['RefreshAll'] = 'Alles vernieuwen'; +$_ADDONLANG['General']['AddRule'] = 'Regel toevoegen'; +$_ADDONLANG['General']['Note'] = 'Opmerking'; +$_ADDONLANG['General']['GB'] = 'GB'; +$_ADDONLANG['General']['TB'] = 'TB'; +$_ADDONLANG['General']['Any'] = 'Elke'; +$_ADDONLANG['General']['EmptyValue'] = '-'; +$_ADDONLANG['General']['Colon'] = ': '; + +## Firewall specific +$_ADDONLANG['Firewall']['CurrentRules'] = 'Huidige Firewall Regels'; +$_ADDONLANG['Firewall']['ResourcesAttached'] = 'Firewall resources zijn gekoppeld aan servers. Als er geen firewall is gekoppeld, wordt er automatisch een aangemaakt wanneer u uw eerste regel toevoegt.'; +$_ADDONLANG['Firewall']['ChangesImmediate'] = 'Firewall wijzigingen worden onmiddellijk toegepast. Het is niet nodig om wijzigingen door te voeren.'; +$_ADDONLANG['Firewall']['AddNewRule'] = 'Nieuwe Firewall Regel toevoegen'; +$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535'; +$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0'; +$_ADDONLANG['Firewall']['DescriptionPlaceholder'] = 'Regel beschrijving'; +$_ADDONLANG['Firewall']['Accept'] = 'ACCEPTEREN'; +$_ADDONLANG['Firewall']['Drop'] = 'WEGGOOIEN'; +$_ADDONLANG['Firewall']['Info'] = 'INFO'; +$_ADDONLANG['Firewall']['Any'] = 'ELKE'; +$_ADDONLANG['Firewall']['TCP'] = 'TCP'; +$_ADDONLANG['Firewall']['UDP'] = 'UDP'; +$_ADDONLANG['Firewall']['ICMP'] = 'ICMP'; + +## Reinstall specific +$_ADDONLANG['Reinstall']['DestroyWarning'] = 'Herbouwen zal alle gegevens op de server vernietigen. Een nieuw root wachtwoord wordt gegenereerd en opgeslagen in uw service account.'; + +## Additional UI messages +$_ADDONLANG['Messages']['PortRequired'] = 'Poort is vereist voor TCP/UDP protocollen'; +$_ADDONLANG['Messages']['RefreshingGraphs'] = 'Alle grafieken vernieuwen...'; +$_ADDONLANG['Messages']['PasswordSaveNote'] = 'Sla dit wachtwoord op een veilige locatie op'; +$_ADDONLANG['Messages']['SelectOSFirst'] = 'Selecteer eerst een besturingssysteem.'; + +## Server types +$_ADDONLANG['ServerTypes']['Standard'] = 'Standaard'; \ No newline at end of file diff --git a/modules/servers/ArkHostHetznerVPS/lang/english.php b/modules/servers/ArkHostHetznerVPS/lang/english.php new file mode 100644 index 0000000..21be202 --- /dev/null +++ b/modules/servers/ArkHostHetznerVPS/lang/english.php @@ -0,0 +1,248 @@ +** The automated backups will also replace your manual backups unless the automated backups are disabled.
*** The automated backups are made 2 times a week and are part of our disaster recovery plan. If you disable the automated backups, you also disable any chance of recovery in case of a disaster.
**** The backup\'s file system might not be fully consistent if the VPS was writing to the filesystem at the moment of the backup. For fully consistent backups, the server must be stopped while the backup is being created.'; +$_ADDONLANG['Backups']['Date'] = 'Date'; +$_ADDONLANG['Backups']['Size'] = 'Size'; +$_ADDONLANG['Backups']['Type'] = 'Type'; +$_ADDONLANG['Backups']['Status'] = 'Status'; +$_ADDONLANG['Backups']['Actions'] = 'Actions'; +$_ADDONLANG['Backups']['Create'] = 'Backup Now'; +$_ADDONLANG['Backups']['Available'] = 'Available'; +$_ADDONLANG['Backups']['Creating'] = 'Creating...'; +$_ADDONLANG['Backups']['Error'] = 'Error'; +$_ADDONLANG['Backups']['Automatic'] = 'Automatic'; +$_ADDONLANG['Backups']['Manual'] = 'Manual'; + +## Settings +$_ADDONLANG['Settings']['Title'] = 'Settings Menu'; +### Hostname +$_ADDONLANG['Settings']['Hostname']['Title'] = 'Hostname'; +$_ADDONLANG['Settings']['Hostname']['Description'] = 'Sets the hostname and the rDNS. Please create the A record first.'; +$_ADDONLANG['Settings']['Hostname']['Submit'] = 'Submit'; + +### ISO +$_ADDONLANG['Settings']['ISO']['Title'] = 'ISO'; +$_ADDONLANG['Settings']['ISO']['Description'] = 'If you install the operating system via the ISO image, you must also configure the network interface statically. There is no DHCP server running.'; +$_ADDONLANG['Settings']['ISO']['Image'] = 'ISO Image'; +$_ADDONLANG['Settings']['ISO']['Submit'] = 'Load ISO'; +$_ADDONLANG['Settings']['ISO']['Remove'] = 'Eject ISO'; + +### Password +$_ADDONLANG['Settings']['Password']['Title'] = 'Password'; +$_ADDONLANG['Settings']['Password']['Description'] = 'The installation password is removed from our systems after 72 hours. It is mandatory for you to change the password on your first login!'; +$_ADDONLANG['Settings']['Password']['Submit'] = 'Reset Password'; + +### Reinstall +$_ADDONLANG['Settings']['Reinstall']['Title'] = 'Reinstall'; +$_ADDONLANG['Settings']['Reinstall']['Description'] = 'Please understand that by reinstalling, all the data will be wiped from the server. This action is irreversible!'; +$_ADDONLANG['Settings']['Reinstall']['OS'] = 'Operating System'; +$_ADDONLANG['Settings']['Reinstall']['Version'] = 'CHOOSE VERSION'; +$_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Reinstall'; + +### Firewall +$_ADDONLANG['Settings']['Firewall']['Title'] = 'Firewall'; +$_ADDONLANG['Settings']['Firewall']['Description'] = 'The rules are evaluated from the top to the bottom. By default, everything is allowed. The firewall is only available on the public interface. Only the inbound traffic will be filtered by the firewall.'; +$_ADDONLANG['Settings']['Firewall']['Action'] = 'Action'; +$_ADDONLANG['Settings']['Firewall']['Port'] = 'Port'; +$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocol'; +$_ADDONLANG['Settings']['Firewall']['Source'] = 'Source'; +$_ADDONLANG['Settings']['Firewall']['Note'] = 'Note'; +$_ADDONLANG['Settings']['Firewall']['Actions'] = 'Actions'; +$_ADDONLANG['Settings']['Firewall']['Accept'] = 'Accept'; +$_ADDONLANG['Settings']['Firewall']['Drop'] = 'Drop'; +$_ADDONLANG['Settings']['Firewall']['PortNumber'] = 'Port Number'; +$_ADDONLANG['Settings']['Firewall']['SourceLabel'] = 'Ex: x.x.x.x/xx (optional)'; +$_ADDONLANG['Settings']['Firewall']['Notes'] = 'Notes (optional)'; +$_ADDONLANG['Settings']['Firewall']['Warning'] = 'The rules must be committed in order to take effect.'; +$_ADDONLANG['Settings']['Firewall']['Submit'] = 'Commit Firewall'; + +### Rescue Mode +$_ADDONLANG['Settings']['Rescue']['Title'] = 'Rescue Mode'; +$_ADDONLANG['Settings']['Rescue']['Description'] = 'Rescue mode boots your server into a temporary Linux system where you can access your server\'s disks to repair or recover data.'; +$_ADDONLANG['Settings']['Rescue']['Status'] = 'Status'; +$_ADDONLANG['Settings']['Rescue']['Active'] = 'Active'; +$_ADDONLANG['Settings']['Rescue']['Inactive'] = 'Inactive'; +$_ADDONLANG['Settings']['Rescue']['Enable'] = 'Enable Rescue Mode'; +$_ADDONLANG['Settings']['Rescue']['EnableReboot'] = 'Enable and Reboot'; +$_ADDONLANG['Settings']['Rescue']['Disable'] = 'Disable Rescue Mode'; +$_ADDONLANG['Settings']['Rescue']['ResetRootPassword'] = 'Reset Root Password'; +$_ADDONLANG['Settings']['Rescue']['Warning'] = 'After enabling rescue mode, you must reboot the server within 60 minutes for it to take effect.'; +$_ADDONLANG['Settings']['Rescue']['PasswordNote'] = 'The root password will be displayed once after enabling rescue mode. Make sure to save it!'; +$_ADDONLANG['Settings']['Rescue']['DisableTitle'] = 'Disable Rescue Mode'; +$_ADDONLANG['Settings']['Rescue']['DisableDescription'] = 'If rescue mode is currently active, you can disable it here. The server will boot normally on next restart.'; +$_ADDONLANG['Settings']['Rescue']['NewRootPassword'] = 'New Root Password'; +$_ADDONLANG['Settings']['Rescue']['AboutTitle'] = 'About Rescue Mode'; +$_ADDONLANG['Settings']['Rescue']['AboutDescription'] = 'The rescue system is a network based environment and can be used to fix issues preventing a regular boot. It is also useful to install custom Linux distributions that are not directly offered by us. You are able to mount the server\'s hard drive inside the rescue system.'; +$_ADDONLANG['Settings']['Rescue']['Important'] = 'Important'; +$_ADDONLANG['Settings']['Rescue']['ImportantNote'] = 'After enabling the rescue system you need to reboot the server in the next 60 minutes to activate it. After another reboot your server will boot from its local disk again.'; +$_ADDONLANG['Settings']['Rescue']['EnableTitle'] = 'Enable Rescue Mode'; +$_ADDONLANG['Settings']['Rescue']['EnableDescription'] = 'Boot into a minimal Linux system for recovery and maintenance tasks.'; +$_ADDONLANG['Settings']['Rescue']['EnableRebootTitle'] = 'Enable & Reboot'; +$_ADDONLANG['Settings']['Rescue']['EnableRebootDescription'] = 'Enable rescue mode and immediately reboot the server to activate it.'; +$_ADDONLANG['Settings']['Rescue']['PasswordPrompt'] = 'Your new root password is:'; + +### Floating IP +$_ADDONLANG['Settings']['FloatingIP']['Title'] = 'Floating IP'; +$_ADDONLANG['Settings']['FloatingIP']['Description'] = 'Manage floating IPs assigned to this server. Floating IPs can be moved between servers.'; +$_ADDONLANG['Settings']['FloatingIP']['Status'] = 'Status'; +$_ADDONLANG['Settings']['FloatingIP']['IP'] = 'IP Address'; +$_ADDONLANG['Settings']['FloatingIP']['Type'] = 'Type'; +$_ADDONLANG['Settings']['FloatingIP']['None'] = 'No floating IP assigned'; +$_ADDONLANG['Settings']['FloatingIP']['NotAvailable'] = 'Floating IP not available for this service'; +$_ADDONLANG['Settings']['FloatingIP']['Assigned'] = 'Assigned'; +$_ADDONLANG['Settings']['FloatingIP']['Unassigned'] = 'Unassigned'; +$_ADDONLANG['Settings']['FloatingIP']['Assign'] = 'Assign to Server'; +$_ADDONLANG['Settings']['FloatingIP']['Unassign'] = 'Unassign from Server'; +$_ADDONLANG['Settings']['FloatingIP']['ReverseDNS'] = 'Reverse DNS'; +$_ADDONLANG['Settings']['FloatingIP']['ReverseDNSDescription'] = 'Set reverse DNS (PTR record) for this floating IP'; +$_ADDONLANG['Settings']['FloatingIP']['SetReverseDNS'] = 'Set Reverse DNS'; +$_ADDONLANG['Settings']['FloatingIP']['ReverseDNSPlaceholder'] = 'example.com'; + +# Additional strings for UI elements +$_ADDONLANG['General']['NoBackupsFound'] = 'No backups found'; +$_ADDONLANG['General']['NoFirewallRules'] = 'No firewall rules configured'; +$_ADDONLANG['General']['NoDataAvailable'] = 'No data available'; +$_ADDONLANG['General']['RefreshAll'] = 'Refresh All'; +$_ADDONLANG['General']['AddRule'] = 'Add Rule'; +$_ADDONLANG['General']['Note'] = 'Note'; +$_ADDONLANG['General']['GB'] = 'GB'; +$_ADDONLANG['General']['TB'] = 'TB'; +$_ADDONLANG['General']['Any'] = 'Any'; +$_ADDONLANG['General']['EmptyValue'] = '-'; +$_ADDONLANG['General']['Colon'] = ': '; + +## Firewall specific +$_ADDONLANG['Firewall']['CurrentRules'] = 'Current Firewall Rules'; +$_ADDONLANG['Firewall']['ResourcesAttached'] = 'Firewall resources are attached to servers. If no firewall is attached, one will be created automatically when you add your first rule.'; +$_ADDONLANG['Firewall']['ChangesImmediate'] = 'Firewall changes are applied immediately. There is no need to commit changes.'; +$_ADDONLANG['Firewall']['AddNewRule'] = 'Add New Firewall Rule'; +$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535'; +$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0'; +$_ADDONLANG['Firewall']['DescriptionPlaceholder'] = 'Rule description'; +$_ADDONLANG['Firewall']['Accept'] = 'ACCEPT'; +$_ADDONLANG['Firewall']['Drop'] = 'DROP'; +$_ADDONLANG['Firewall']['Info'] = 'INFO'; +$_ADDONLANG['Firewall']['Any'] = 'ANY'; +$_ADDONLANG['Firewall']['TCP'] = 'TCP'; +$_ADDONLANG['Firewall']['UDP'] = 'UDP'; +$_ADDONLANG['Firewall']['ICMP'] = 'ICMP'; + +## Reinstall specific +$_ADDONLANG['Reinstall']['DestroyWarning'] = 'Rebuilding will destroy all data on the server. A new root password will be generated and saved to your service account.'; + +## Additional UI messages +$_ADDONLANG['Messages']['PortRequired'] = 'Port is required for TCP/UDP protocols'; +$_ADDONLANG['Messages']['RefreshingGraphs'] = 'Refreshing all graphs...'; +$_ADDONLANG['Messages']['PasswordSaveNote'] = 'Save this password in a secure location'; +$_ADDONLANG['Messages']['SelectOSFirst'] = 'Please select an operating system first.'; + +## Server types +$_ADDONLANG['ServerTypes']['Standard'] = 'Standard'; diff --git a/modules/servers/ArkHostHetznerVPS/lang/french.php b/modules/servers/ArkHostHetznerVPS/lang/french.php new file mode 100644 index 0000000..aac3c01 --- /dev/null +++ b/modules/servers/ArkHostHetznerVPS/lang/french.php @@ -0,0 +1,231 @@ +** Les sauvegardes automatiques remplaceront également vos sauvegardes manuelles à moins que les sauvegardes automatiques ne soient désactivées.
*** Les sauvegardes automatiques sont effectuées 2 fois par semaine et font partie de notre plan de récupération après sinistre. Si vous désactivez les sauvegardes automatiques, vous désactivez également toute chance de récupération en cas de sinistre.
**** Le système de fichiers de la sauvegarde pourrait ne pas être entièrement cohérent si le VPS écrivait sur le système de fichiers au moment de la sauvegarde. Pour des sauvegardes entièrement cohérentes, le serveur doit être arrêté pendant la création de la sauvegarde.'; +$_ADDONLANG['Backups']['Date'] = 'Date'; +$_ADDONLANG['Backups']['Size'] = 'Taille'; +$_ADDONLANG['Backups']['Type'] = 'Type'; +$_ADDONLANG['Backups']['Status'] = 'Statut'; +$_ADDONLANG['Backups']['Actions'] = 'Actions'; +$_ADDONLANG['Backups']['Create'] = 'Sauvegarder Maintenant'; +$_ADDONLANG['Backups']['Available'] = 'Disponible'; +$_ADDONLANG['Backups']['Creating'] = 'Création...'; +$_ADDONLANG['Backups']['Error'] = 'Erreur'; +$_ADDONLANG['Backups']['Automatic'] = 'Automatique'; +$_ADDONLANG['Backups']['Manual'] = 'Manuel'; + +## Settings +$_ADDONLANG['Settings']['Title'] = 'Menu des Paramètres'; +### Hostname +$_ADDONLANG['Settings']['Hostname']['Title'] = 'Nom d\'hôte'; +$_ADDONLANG['Settings']['Hostname']['Description'] = 'Définit le nom d\'hôte et le rDNS. Veuillez d\'abord créer l\'enregistrement A.'; +$_ADDONLANG['Settings']['Hostname']['Submit'] = 'Soumettre'; + +### ISO +$_ADDONLANG['Settings']['ISO']['Title'] = 'ISO'; +$_ADDONLANG['Settings']['ISO']['Description'] = 'Si vous installez le système d\'exploitation via l\'image ISO, vous devez également configurer l\'interface réseau de manière statique. Il n\'y a pas de serveur DHCP en cours d\'exécution.'; +$_ADDONLANG['Settings']['ISO']['Image'] = 'Image ISO'; +$_ADDONLANG['Settings']['ISO']['Submit'] = 'Charger ISO'; +$_ADDONLANG['Settings']['ISO']['Remove'] = 'Éjecter ISO'; + +### Password +$_ADDONLANG['Settings']['Password']['Title'] = 'Mot de Passe'; +$_ADDONLANG['Settings']['Password']['Description'] = 'Le mot de passe d\'installation est supprimé de nos systèmes après 72 heures. Il est obligatoire de changer le mot de passe lors de votre première connexion !'; +$_ADDONLANG['Settings']['Password']['Submit'] = 'Réinitialiser le Mot de Passe'; + +### Reinstall +$_ADDONLANG['Settings']['Reinstall']['Title'] = 'Réinstaller'; +$_ADDONLANG['Settings']['Reinstall']['Description'] = 'Veuillez comprendre qu\'en réinstallant, toutes les données seront effacées du serveur. Cette action est irréversible !'; +$_ADDONLANG['Settings']['Reinstall']['OS'] = 'Système d\'Exploitation'; +$_ADDONLANG['Settings']['Reinstall']['Version'] = 'CHOISIR LA VERSION'; +$_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Réinstaller'; + +### Firewall +$_ADDONLANG['Settings']['Firewall']['Title'] = 'Pare-feu'; +$_ADDONLANG['Settings']['Firewall']['Description'] = 'Les règles sont évaluées de haut en bas. Par défaut, tout est autorisé. Le pare-feu n\'est disponible que sur l\'interface publique. Seul le trafic entrant sera filtré par le pare-feu.'; +$_ADDONLANG['Settings']['Firewall']['Action'] = 'Action'; +$_ADDONLANG['Settings']['Firewall']['Port'] = 'Port'; +$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocole'; +$_ADDONLANG['Settings']['Firewall']['Source'] = 'Source'; +$_ADDONLANG['Settings']['Firewall']['Note'] = 'Note'; +$_ADDONLANG['Settings']['Firewall']['Actions'] = 'Actions'; +$_ADDONLANG['Settings']['Firewall']['Accept'] = 'Accepter'; +$_ADDONLANG['Settings']['Firewall']['Drop'] = 'Rejeter'; +$_ADDONLANG['Settings']['Firewall']['PortNumber'] = 'Numéro de Port'; +$_ADDONLANG['Settings']['Firewall']['SourceLabel'] = 'Ex : x.x.x.x/xx (optionnel)'; +$_ADDONLANG['Settings']['Firewall']['Notes'] = 'Notes (optionnel)'; +$_ADDONLANG['Settings']['Firewall']['Warning'] = 'Les règles doivent être validées pour prendre effet.'; +$_ADDONLANG['Settings']['Firewall']['Submit'] = 'Valider le Pare-feu'; + +### Rescue Mode +$_ADDONLANG['Settings']['Rescue']['Title'] = 'Mode de Secours'; +$_ADDONLANG['Settings']['Rescue']['Description'] = 'Le mode de secours démarre votre serveur dans un système Linux temporaire où vous pouvez accéder aux disques de votre serveur pour réparer ou récupérer des données.'; +$_ADDONLANG['Settings']['Rescue']['Status'] = 'Statut'; +$_ADDONLANG['Settings']['Rescue']['Active'] = 'Actif'; +$_ADDONLANG['Settings']['Rescue']['Inactive'] = 'Inactif'; +$_ADDONLANG['Settings']['Rescue']['Enable'] = 'Activer le Mode de Secours'; +$_ADDONLANG['Settings']['Rescue']['EnableReboot'] = 'Activer et Redémarrer'; +$_ADDONLANG['Settings']['Rescue']['Disable'] = 'Désactiver le Mode de Secours'; +$_ADDONLANG['Settings']['Rescue']['ResetRootPassword'] = 'Réinitialiser le Mot de Passe Root'; +$_ADDONLANG['Settings']['Rescue']['Warning'] = 'Après avoir activé le mode de secours, vous devez redémarrer le serveur dans les 60 minutes pour qu\'il prenne effet.'; +$_ADDONLANG['Settings']['Rescue']['PasswordNote'] = 'Le mot de passe root sera affiché une fois après l\'activation du mode de secours. Assurez-vous de le sauvegarder !'; +$_ADDONLANG['Settings']['Rescue']['DisableTitle'] = 'Désactiver le Mode de Secours'; +$_ADDONLANG['Settings']['Rescue']['DisableDescription'] = 'Si le mode de secours est actuellement actif, vous pouvez le désactiver ici. Le serveur démarrera normalement au prochain redémarrage.'; +$_ADDONLANG['Settings']['Rescue']['NewRootPassword'] = 'Nouveau Mot de Passe Root'; +$_ADDONLANG['Settings']['Rescue']['AboutTitle'] = 'À Propos du Mode de Secours'; +$_ADDONLANG['Settings']['Rescue']['AboutDescription'] = 'Le système de secours est un environnement basé sur le réseau et peut être utilisé pour résoudre les problèmes empêchant un démarrage normal. Il est également utile pour installer des distributions Linux personnalisées qui ne sont pas directement offertes par nous. Vous pouvez monter le disque dur du serveur à l\'intérieur du système de secours.'; +$_ADDONLANG['Settings']['Rescue']['Important'] = 'Important'; +$_ADDONLANG['Settings']['Rescue']['ImportantNote'] = 'Après avoir activé le système de secours, vous devez redémarrer le serveur dans les 60 minutes suivantes pour l\'activer. Après un autre redémarrage, votre serveur démarrera à nouveau depuis son disque local.'; +$_ADDONLANG['Settings']['Rescue']['EnableTitle'] = 'Activer le Mode de Secours'; +$_ADDONLANG['Settings']['Rescue']['EnableDescription'] = 'Démarrer dans un système Linux minimal pour les tâches de récupération et de maintenance.'; +$_ADDONLANG['Settings']['Rescue']['EnableRebootTitle'] = 'Activer et Redémarrer'; +$_ADDONLANG['Settings']['Rescue']['EnableRebootDescription'] = 'Activer le mode de secours et redémarrer immédiatement le serveur pour l\'activer.'; +$_ADDONLANG['Settings']['Rescue']['PasswordPrompt'] = 'Votre nouveau mot de passe root est :'; + +# Additional strings for UI elements +$_ADDONLANG['General']['NoBackupsFound'] = 'Aucune sauvegarde trouvée'; +$_ADDONLANG['General']['NoFirewallRules'] = 'Aucune règle de pare-feu configurée'; +$_ADDONLANG['General']['NoDataAvailable'] = 'Aucune donnée disponible'; +$_ADDONLANG['General']['RefreshAll'] = 'Actualiser Tout'; +$_ADDONLANG['General']['AddRule'] = 'Ajouter une Règle'; +$_ADDONLANG['General']['Note'] = 'Note'; +$_ADDONLANG['General']['GB'] = 'Go'; +$_ADDONLANG['General']['TB'] = 'To'; +$_ADDONLANG['General']['Any'] = 'Tout'; +$_ADDONLANG['General']['EmptyValue'] = '-'; +$_ADDONLANG['General']['Colon'] = ' : '; + +## Firewall specific +$_ADDONLANG['Firewall']['CurrentRules'] = 'Règles Actuelles du Pare-feu'; +$_ADDONLANG['Firewall']['ResourcesAttached'] = 'Les ressources de pare-feu sont attachées aux serveurs. Si aucun pare-feu n\'est attaché, un sera créé automatiquement lorsque vous ajouterez votre première règle.'; +$_ADDONLANG['Firewall']['ChangesImmediate'] = 'Les changements du pare-feu sont appliqués immédiatement. Il n\'y a pas besoin de valider les changements.'; +$_ADDONLANG['Firewall']['AddNewRule'] = 'Ajouter une Nouvelle Règle de Pare-feu'; +$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535'; +$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0'; +$_ADDONLANG['Firewall']['DescriptionPlaceholder'] = 'Description de la règle'; +$_ADDONLANG['Firewall']['Accept'] = 'ACCEPTER'; +$_ADDONLANG['Firewall']['Drop'] = 'REJETER'; +$_ADDONLANG['Firewall']['Info'] = 'INFO'; +$_ADDONLANG['Firewall']['Any'] = 'TOUT'; +$_ADDONLANG['Firewall']['TCP'] = 'TCP'; +$_ADDONLANG['Firewall']['UDP'] = 'UDP'; +$_ADDONLANG['Firewall']['ICMP'] = 'ICMP'; + +## Reinstall specific +$_ADDONLANG['Reinstall']['DestroyWarning'] = 'La reconstruction détruira toutes les données sur le serveur. Un nouveau mot de passe root sera généré et sauvegardé dans votre compte de service.'; + +## Additional UI messages +$_ADDONLANG['Messages']['PortRequired'] = 'Le port est requis pour les protocoles TCP/UDP'; +$_ADDONLANG['Messages']['RefreshingGraphs'] = 'Actualisation de tous les graphiques...'; +$_ADDONLANG['Messages']['PasswordSaveNote'] = 'Sauvegardez ce mot de passe dans un endroit sûr'; +$_ADDONLANG['Messages']['SelectOSFirst'] = 'Veuillez d\'abord sélectionner un système d\'exploitation.'; + +## Server types +$_ADDONLANG['ServerTypes']['Standard'] = 'Standard'; \ No newline at end of file diff --git a/modules/servers/ArkHostHetznerVPS/lang/german.php b/modules/servers/ArkHostHetznerVPS/lang/german.php new file mode 100644 index 0000000..65c9a78 --- /dev/null +++ b/modules/servers/ArkHostHetznerVPS/lang/german.php @@ -0,0 +1,231 @@ +** Die automatischen Backups ersetzen auch Ihre manuellen Backups, es sei denn, die automatischen Backups sind deaktiviert.
*** Die automatischen Backups werden 2 Mal pro Woche erstellt und sind Teil unseres Disaster Recovery Plans. Wenn Sie die automatischen Backups deaktivieren, deaktivieren Sie auch jede Chance auf Wiederherstellung im Katastrophenfall.
**** Das Dateisystem des Backups ist möglicherweise nicht vollständig konsistent, wenn der VPS zum Zeitpunkt des Backups ins Dateisystem geschrieben hat. Für vollständig konsistente Backups muss der Server während der Backup-Erstellung gestoppt werden.'; +$_ADDONLANG['Backups']['Date'] = 'Datum'; +$_ADDONLANG['Backups']['Size'] = 'Größe'; +$_ADDONLANG['Backups']['Type'] = 'Typ'; +$_ADDONLANG['Backups']['Status'] = 'Status'; +$_ADDONLANG['Backups']['Actions'] = 'Aktionen'; +$_ADDONLANG['Backups']['Create'] = 'Jetzt sichern'; +$_ADDONLANG['Backups']['Available'] = 'Verfügbar'; +$_ADDONLANG['Backups']['Creating'] = 'Wird erstellt...'; +$_ADDONLANG['Backups']['Error'] = 'Fehler'; +$_ADDONLANG['Backups']['Automatic'] = 'Automatisch'; +$_ADDONLANG['Backups']['Manual'] = 'Manuell'; + +## Settings +$_ADDONLANG['Settings']['Title'] = 'Einstellungsmenü'; +### Hostname +$_ADDONLANG['Settings']['Hostname']['Title'] = 'Hostname'; +$_ADDONLANG['Settings']['Hostname']['Description'] = 'Setzt den Hostname und das rDNS. Bitte erstellen Sie zuerst den A-Record.'; +$_ADDONLANG['Settings']['Hostname']['Submit'] = 'Absenden'; + +### ISO +$_ADDONLANG['Settings']['ISO']['Title'] = 'ISO'; +$_ADDONLANG['Settings']['ISO']['Description'] = 'Wenn Sie das Betriebssystem über das ISO-Image installieren, müssen Sie auch die Netzwerkschnittstelle statisch konfigurieren. Es läuft kein DHCP-Server.'; +$_ADDONLANG['Settings']['ISO']['Image'] = 'ISO-Image'; +$_ADDONLANG['Settings']['ISO']['Submit'] = 'ISO laden'; +$_ADDONLANG['Settings']['ISO']['Remove'] = 'ISO auswerfen'; + +### Password +$_ADDONLANG['Settings']['Password']['Title'] = 'Passwort'; +$_ADDONLANG['Settings']['Password']['Description'] = 'Das Installationspasswort wird nach 72 Stunden aus unseren Systemen entfernt. Es ist obligatorisch, dass Sie das Passwort bei Ihrem ersten Login ändern!'; +$_ADDONLANG['Settings']['Password']['Submit'] = 'Passwort zurücksetzen'; + +### Reinstall +$_ADDONLANG['Settings']['Reinstall']['Title'] = 'Neuinstallation'; +$_ADDONLANG['Settings']['Reinstall']['Description'] = 'Bitte beachten Sie, dass bei einer Neuinstallation alle Daten vom Server gelöscht werden. Diese Aktion ist irreversibel!'; +$_ADDONLANG['Settings']['Reinstall']['OS'] = 'Betriebssystem'; +$_ADDONLANG['Settings']['Reinstall']['Version'] = 'VERSION WÄHLEN'; +$_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Neuinstallieren'; + +### Firewall +$_ADDONLANG['Settings']['Firewall']['Title'] = 'Firewall'; +$_ADDONLANG['Settings']['Firewall']['Description'] = 'Die Regeln werden von oben nach unten ausgewertet. Standardmäßig ist alles erlaubt. Die Firewall ist nur auf der öffentlichen Schnittstelle verfügbar. Nur der eingehende Traffic wird von der Firewall gefiltert.'; +$_ADDONLANG['Settings']['Firewall']['Action'] = 'Aktion'; +$_ADDONLANG['Settings']['Firewall']['Port'] = 'Port'; +$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protokoll'; +$_ADDONLANG['Settings']['Firewall']['Source'] = 'Quelle'; +$_ADDONLANG['Settings']['Firewall']['Note'] = 'Hinweis'; +$_ADDONLANG['Settings']['Firewall']['Actions'] = 'Aktionen'; +$_ADDONLANG['Settings']['Firewall']['Accept'] = 'Akzeptieren'; +$_ADDONLANG['Settings']['Firewall']['Drop'] = 'Verwerfen'; +$_ADDONLANG['Settings']['Firewall']['PortNumber'] = 'Port-Nummer'; +$_ADDONLANG['Settings']['Firewall']['SourceLabel'] = 'z.B.: x.x.x.x/xx (optional)'; +$_ADDONLANG['Settings']['Firewall']['Notes'] = 'Hinweise (optional)'; +$_ADDONLANG['Settings']['Firewall']['Warning'] = 'Die Regeln müssen übernommen werden, um wirksam zu werden.'; +$_ADDONLANG['Settings']['Firewall']['Submit'] = 'Firewall übernehmen'; + +### Rescue Mode +$_ADDONLANG['Settings']['Rescue']['Title'] = 'Rettungsmodus'; +$_ADDONLANG['Settings']['Rescue']['Description'] = 'Der Rettungsmodus startet Ihren Server in ein temporäres Linux-System, von dem aus Sie auf die Festplatten Ihres Servers zugreifen können, um Daten zu reparieren oder wiederherzustellen.'; +$_ADDONLANG['Settings']['Rescue']['Status'] = 'Status'; +$_ADDONLANG['Settings']['Rescue']['Active'] = 'Aktiv'; +$_ADDONLANG['Settings']['Rescue']['Inactive'] = 'Inaktiv'; +$_ADDONLANG['Settings']['Rescue']['Enable'] = 'Rettungsmodus aktivieren'; +$_ADDONLANG['Settings']['Rescue']['EnableReboot'] = 'Aktivieren und neustarten'; +$_ADDONLANG['Settings']['Rescue']['Disable'] = 'Rettungsmodus deaktivieren'; +$_ADDONLANG['Settings']['Rescue']['ResetRootPassword'] = 'Root-Passwort zurücksetzen'; +$_ADDONLANG['Settings']['Rescue']['Warning'] = 'Nach dem Aktivieren des Rettungsmodus müssen Sie den Server innerhalb von 60 Minuten neu starten, damit er wirksam wird.'; +$_ADDONLANG['Settings']['Rescue']['PasswordNote'] = 'Das Root-Passwort wird nach dem Aktivieren des Rettungsmodus einmal angezeigt. Stellen Sie sicher, dass Sie es speichern!'; +$_ADDONLANG['Settings']['Rescue']['DisableTitle'] = 'Rettungsmodus deaktivieren'; +$_ADDONLANG['Settings']['Rescue']['DisableDescription'] = 'Wenn der Rettungsmodus derzeit aktiv ist, können Sie ihn hier deaktivieren. Der Server wird beim nächsten Neustart normal starten.'; +$_ADDONLANG['Settings']['Rescue']['NewRootPassword'] = 'Neues Root-Passwort'; +$_ADDONLANG['Settings']['Rescue']['AboutTitle'] = 'Über den Rettungsmodus'; +$_ADDONLANG['Settings']['Rescue']['AboutDescription'] = 'Das Rettungssystem ist eine netzwerkbasierte Umgebung und kann verwendet werden, um Probleme zu beheben, die einen regulären Start verhindern. Es ist auch nützlich für die Installation benutzerdefinierter Linux-Distributionen, die nicht direkt von uns angeboten werden. Sie können die Festplatte des Servers im Rettungssystem mounten.'; +$_ADDONLANG['Settings']['Rescue']['Important'] = 'Wichtig'; +$_ADDONLANG['Settings']['Rescue']['ImportantNote'] = 'Nach dem Aktivieren des Rettungssystems müssen Sie den Server in den nächsten 60 Minuten neu starten, um es zu aktivieren. Nach einem weiteren Neustart startet Ihr Server wieder von seiner lokalen Festplatte.'; +$_ADDONLANG['Settings']['Rescue']['EnableTitle'] = 'Rettungsmodus aktivieren'; +$_ADDONLANG['Settings']['Rescue']['EnableDescription'] = 'In ein minimales Linux-System für Wiederherstellungs- und Wartungsaufgaben starten.'; +$_ADDONLANG['Settings']['Rescue']['EnableRebootTitle'] = 'Aktivieren & Neustarten'; +$_ADDONLANG['Settings']['Rescue']['EnableRebootDescription'] = 'Rettungsmodus aktivieren und den Server sofort neu starten, um ihn zu aktivieren.'; +$_ADDONLANG['Settings']['Rescue']['PasswordPrompt'] = 'Ihr neues Root-Passwort lautet:'; + +# Additional strings for UI elements +$_ADDONLANG['General']['NoBackupsFound'] = 'Keine Backups gefunden'; +$_ADDONLANG['General']['NoFirewallRules'] = 'Keine Firewall-Regeln konfiguriert'; +$_ADDONLANG['General']['NoDataAvailable'] = 'Keine Daten verfügbar'; +$_ADDONLANG['General']['RefreshAll'] = 'Alle aktualisieren'; +$_ADDONLANG['General']['AddRule'] = 'Regel hinzufügen'; +$_ADDONLANG['General']['Note'] = 'Hinweis'; +$_ADDONLANG['General']['GB'] = 'GB'; +$_ADDONLANG['General']['TB'] = 'TB'; +$_ADDONLANG['General']['Any'] = 'Alle'; +$_ADDONLANG['General']['EmptyValue'] = '-'; +$_ADDONLANG['General']['Colon'] = ': '; + +## Firewall specific +$_ADDONLANG['Firewall']['CurrentRules'] = 'Aktuelle Firewall-Regeln'; +$_ADDONLANG['Firewall']['ResourcesAttached'] = 'Firewall-Ressourcen sind an Server angehängt. Wenn keine Firewall angehängt ist, wird automatisch eine erstellt, wenn Sie Ihre erste Regel hinzufügen.'; +$_ADDONLANG['Firewall']['ChangesImmediate'] = 'Firewall-Änderungen werden sofort angewendet. Es ist nicht notwendig, Änderungen zu übernehmen.'; +$_ADDONLANG['Firewall']['AddNewRule'] = 'Neue Firewall-Regel hinzufügen'; +$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535'; +$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0'; +$_ADDONLANG['Firewall']['DescriptionPlaceholder'] = 'Regelbeschreibung'; +$_ADDONLANG['Firewall']['Accept'] = 'AKZEPTIEREN'; +$_ADDONLANG['Firewall']['Drop'] = 'VERWERFEN'; +$_ADDONLANG['Firewall']['Info'] = 'INFO'; +$_ADDONLANG['Firewall']['Any'] = 'ALLE'; +$_ADDONLANG['Firewall']['TCP'] = 'TCP'; +$_ADDONLANG['Firewall']['UDP'] = 'UDP'; +$_ADDONLANG['Firewall']['ICMP'] = 'ICMP'; + +## Reinstall specific +$_ADDONLANG['Reinstall']['DestroyWarning'] = 'Die Neuinstallation wird alle Daten auf dem Server zerstören. Ein neues Root-Passwort wird generiert und in Ihrem Service-Konto gespeichert.'; + +## Additional UI messages +$_ADDONLANG['Messages']['PortRequired'] = 'Port ist für TCP/UDP-Protokolle erforderlich'; +$_ADDONLANG['Messages']['RefreshingGraphs'] = 'Alle Diagramme werden aktualisiert...'; +$_ADDONLANG['Messages']['PasswordSaveNote'] = 'Speichern Sie dieses Passwort an einem sicheren Ort'; +$_ADDONLANG['Messages']['SelectOSFirst'] = 'Bitte wählen Sie zuerst ein Betriebssystem aus.'; + +## Server types +$_ADDONLANG['ServerTypes']['Standard'] = 'Standard'; \ No newline at end of file diff --git a/modules/servers/ArkHostHetznerVPS/lang/italian.php b/modules/servers/ArkHostHetznerVPS/lang/italian.php new file mode 100644 index 0000000..981fec5 --- /dev/null +++ b/modules/servers/ArkHostHetznerVPS/lang/italian.php @@ -0,0 +1,231 @@ +** I backup automatici sostituiranno anche i tuoi backup manuali a meno che i backup automatici non siano disabilitati.
*** I backup automatici vengono eseguiti 2 volte a settimana e fanno parte del nostro piano di disaster recovery. Se disabiliti i backup automatici, disabiliti anche qualsiasi possibilità di recupero in caso di disastro.
**** Il file system del backup potrebbe non essere completamente coerente se il VPS stava scrivendo sul filesystem al momento del backup. Per backup completamente coerenti, il server deve essere fermato durante la creazione del backup.'; +$_ADDONLANG['Backups']['Date'] = 'Data'; +$_ADDONLANG['Backups']['Size'] = 'Dimensione'; +$_ADDONLANG['Backups']['Type'] = 'Tipo'; +$_ADDONLANG['Backups']['Status'] = 'Stato'; +$_ADDONLANG['Backups']['Actions'] = 'Azioni'; +$_ADDONLANG['Backups']['Create'] = 'Backup Ora'; +$_ADDONLANG['Backups']['Available'] = 'Disponibile'; +$_ADDONLANG['Backups']['Creating'] = 'Creazione in corso...'; +$_ADDONLANG['Backups']['Error'] = 'Errore'; +$_ADDONLANG['Backups']['Automatic'] = 'Automatico'; +$_ADDONLANG['Backups']['Manual'] = 'Manuale'; + +## Settings +$_ADDONLANG['Settings']['Title'] = 'Menu Impostazioni'; +### Hostname +$_ADDONLANG['Settings']['Hostname']['Title'] = 'Nome Host'; +$_ADDONLANG['Settings']['Hostname']['Description'] = 'Imposta il nome host e il rDNS. Crea prima il record A.'; +$_ADDONLANG['Settings']['Hostname']['Submit'] = 'Invia'; + +### ISO +$_ADDONLANG['Settings']['ISO']['Title'] = 'ISO'; +$_ADDONLANG['Settings']['ISO']['Description'] = 'Se installi il sistema operativo tramite l\'immagine ISO, devi anche configurare staticamente l\'interfaccia di rete. Non c\'è un server DHCP in funzione.'; +$_ADDONLANG['Settings']['ISO']['Image'] = 'Immagine ISO'; +$_ADDONLANG['Settings']['ISO']['Submit'] = 'Carica ISO'; +$_ADDONLANG['Settings']['ISO']['Remove'] = 'Espelli ISO'; + +### Password +$_ADDONLANG['Settings']['Password']['Title'] = 'Password'; +$_ADDONLANG['Settings']['Password']['Description'] = 'La password di installazione viene rimossa dai nostri sistemi dopo 72 ore. È obbligatorio cambiare la password al primo accesso!'; +$_ADDONLANG['Settings']['Password']['Submit'] = 'Reimposta Password'; + +### Reinstall +$_ADDONLANG['Settings']['Reinstall']['Title'] = 'Reinstallazione'; +$_ADDONLANG['Settings']['Reinstall']['Description'] = 'Ti preghiamo di capire che reinstallando, tutti i dati verranno cancellati dal server. Questa azione è irreversibile!'; +$_ADDONLANG['Settings']['Reinstall']['OS'] = 'Sistema Operativo'; +$_ADDONLANG['Settings']['Reinstall']['Version'] = 'SCEGLI VERSIONE'; +$_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Reinstalla'; + +### Firewall +$_ADDONLANG['Settings']['Firewall']['Title'] = 'Firewall'; +$_ADDONLANG['Settings']['Firewall']['Description'] = 'Le regole vengono valutate dall\'alto verso il basso. Per impostazione predefinita, tutto è consentito. Il firewall è disponibile solo sull\'interfaccia pubblica. Solo il traffico in entrata verrà filtrato dal firewall.'; +$_ADDONLANG['Settings']['Firewall']['Action'] = 'Azione'; +$_ADDONLANG['Settings']['Firewall']['Port'] = 'Porta'; +$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocollo'; +$_ADDONLANG['Settings']['Firewall']['Source'] = 'Sorgente'; +$_ADDONLANG['Settings']['Firewall']['Note'] = 'Nota'; +$_ADDONLANG['Settings']['Firewall']['Actions'] = 'Azioni'; +$_ADDONLANG['Settings']['Firewall']['Accept'] = 'Accetta'; +$_ADDONLANG['Settings']['Firewall']['Drop'] = 'Rilascia'; +$_ADDONLANG['Settings']['Firewall']['PortNumber'] = 'Numero Porta'; +$_ADDONLANG['Settings']['Firewall']['SourceLabel'] = 'Es: x.x.x.x/xx (opzionale)'; +$_ADDONLANG['Settings']['Firewall']['Notes'] = 'Note (opzionale)'; +$_ADDONLANG['Settings']['Firewall']['Warning'] = 'Le regole devono essere confermate per avere effetto.'; +$_ADDONLANG['Settings']['Firewall']['Submit'] = 'Conferma Firewall'; + +### Rescue Mode +$_ADDONLANG['Settings']['Rescue']['Title'] = 'Modalità di Recupero'; +$_ADDONLANG['Settings']['Rescue']['Description'] = 'La modalità di recupero avvia il tuo server in un sistema Linux temporaneo dove puoi accedere ai dischi del tuo server per riparare o recuperare dati.'; +$_ADDONLANG['Settings']['Rescue']['Status'] = 'Stato'; +$_ADDONLANG['Settings']['Rescue']['Active'] = 'Attivo'; +$_ADDONLANG['Settings']['Rescue']['Inactive'] = 'Inattivo'; +$_ADDONLANG['Settings']['Rescue']['Enable'] = 'Attiva Modalità di Recupero'; +$_ADDONLANG['Settings']['Rescue']['EnableReboot'] = 'Attiva e Riavvia'; +$_ADDONLANG['Settings']['Rescue']['Disable'] = 'Disattiva Modalità di Recupero'; +$_ADDONLANG['Settings']['Rescue']['ResetRootPassword'] = 'Reimposta Password Root'; +$_ADDONLANG['Settings']['Rescue']['Warning'] = 'Dopo aver attivato la modalità di recupero, devi riavviare il server entro 60 minuti perché abbia effetto.'; +$_ADDONLANG['Settings']['Rescue']['PasswordNote'] = 'La password root verrà visualizzata una volta dopo aver attivato la modalità di recupero. Assicurati di salvarla!'; +$_ADDONLANG['Settings']['Rescue']['DisableTitle'] = 'Disattiva Modalità di Recupero'; +$_ADDONLANG['Settings']['Rescue']['DisableDescription'] = 'Se la modalità di recupero è attualmente attiva, puoi disattivarla qui. Il server si avvierà normalmente al prossimo riavvio.'; +$_ADDONLANG['Settings']['Rescue']['NewRootPassword'] = 'Nuova Password Root'; +$_ADDONLANG['Settings']['Rescue']['AboutTitle'] = 'Informazioni sulla Modalità di Recupero'; +$_ADDONLANG['Settings']['Rescue']['AboutDescription'] = 'Il sistema di recupero è un ambiente basato su rete e può essere utilizzato per risolvere problemi che impediscono un avvio regolare. È anche utile per installare distribuzioni Linux personalizzate che non sono offerte direttamente da noi. Puoi montare il disco rigido del server all\'interno del sistema di recupero.'; +$_ADDONLANG['Settings']['Rescue']['Important'] = 'Importante'; +$_ADDONLANG['Settings']['Rescue']['ImportantNote'] = 'Dopo aver attivato il sistema di recupero devi riavviare il server nei prossimi 60 minuti per attivarlo. Dopo un altro riavvio il tuo server si avvierà di nuovo dal suo disco locale.'; +$_ADDONLANG['Settings']['Rescue']['EnableTitle'] = 'Attiva Modalità di Recupero'; +$_ADDONLANG['Settings']['Rescue']['EnableDescription'] = 'Avvia in un sistema Linux minimale per attività di recupero e manutenzione.'; +$_ADDONLANG['Settings']['Rescue']['EnableRebootTitle'] = 'Attiva e Riavvia'; +$_ADDONLANG['Settings']['Rescue']['EnableRebootDescription'] = 'Attiva la modalità di recupero e riavvia immediatamente il server per attivarla.'; +$_ADDONLANG['Settings']['Rescue']['PasswordPrompt'] = 'La tua nuova password root è:'; + +# Additional strings for UI elements +$_ADDONLANG['General']['NoBackupsFound'] = 'Nessun backup trovato'; +$_ADDONLANG['General']['NoFirewallRules'] = 'Nessuna regola firewall configurata'; +$_ADDONLANG['General']['NoDataAvailable'] = 'Nessun dato disponibile'; +$_ADDONLANG['General']['RefreshAll'] = 'Aggiorna Tutto'; +$_ADDONLANG['General']['AddRule'] = 'Aggiungi Regola'; +$_ADDONLANG['General']['Note'] = 'Nota'; +$_ADDONLANG['General']['GB'] = 'GB'; +$_ADDONLANG['General']['TB'] = 'TB'; +$_ADDONLANG['General']['Any'] = 'Qualsiasi'; +$_ADDONLANG['General']['EmptyValue'] = '-'; +$_ADDONLANG['General']['Colon'] = ': '; + +## Firewall specific +$_ADDONLANG['Firewall']['CurrentRules'] = 'Regole Firewall Attuali'; +$_ADDONLANG['Firewall']['ResourcesAttached'] = 'Le risorse del firewall sono collegate ai server. Se nessun firewall è collegato, ne verrà creato automaticamente uno quando aggiungi la tua prima regola.'; +$_ADDONLANG['Firewall']['ChangesImmediate'] = 'Le modifiche al firewall vengono applicate immediatamente. Non è necessario confermare le modifiche.'; +$_ADDONLANG['Firewall']['AddNewRule'] = 'Aggiungi Nuova Regola Firewall'; +$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535'; +$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0'; +$_ADDONLANG['Firewall']['DescriptionPlaceholder'] = 'Descrizione regola'; +$_ADDONLANG['Firewall']['Accept'] = 'ACCETTA'; +$_ADDONLANG['Firewall']['Drop'] = 'RILASCIA'; +$_ADDONLANG['Firewall']['Info'] = 'INFO'; +$_ADDONLANG['Firewall']['Any'] = 'QUALSIASI'; +$_ADDONLANG['Firewall']['TCP'] = 'TCP'; +$_ADDONLANG['Firewall']['UDP'] = 'UDP'; +$_ADDONLANG['Firewall']['ICMP'] = 'ICMP'; + +## Reinstall specific +$_ADDONLANG['Reinstall']['DestroyWarning'] = 'La ricostruzione distruggerà tutti i dati sul server. Una nuova password root verrà generata e salvata nel tuo account di servizio.'; + +## Additional UI messages +$_ADDONLANG['Messages']['PortRequired'] = 'La porta è richiesta per i protocolli TCP/UDP'; +$_ADDONLANG['Messages']['RefreshingGraphs'] = 'Aggiornamento di tutti i grafici...'; +$_ADDONLANG['Messages']['PasswordSaveNote'] = 'Salva questa password in un luogo sicuro'; +$_ADDONLANG['Messages']['SelectOSFirst'] = 'Seleziona prima un sistema operativo.'; + +## Server types +$_ADDONLANG['ServerTypes']['Standard'] = 'Standard'; \ No newline at end of file diff --git a/modules/servers/ArkHostHetznerVPS/lang/portuguese-pt.php b/modules/servers/ArkHostHetznerVPS/lang/portuguese-pt.php new file mode 100644 index 0000000..df7f2d6 --- /dev/null +++ b/modules/servers/ArkHostHetznerVPS/lang/portuguese-pt.php @@ -0,0 +1,248 @@ +** As cópias de segurança automatizadas também substituirão as suas cópias de segurança manuais, a menos que as cópias de segurança automatizadas estejam desactivadas.
*** As cópias de segurança automatizadas são feitas 2 vezes por semana e fazem parte do nosso plano de recuperação de desastres. Se desactivar as cópias de segurança automatizadas, também desactiva qualquer hipótese de recuperação em caso de desastre.
**** O sistema de ficheiros da cópia de segurança pode não estar completamente consistente se o VPS estivesse a escrever no sistema de ficheiros no momento da cópia de segurança. Para cópias de segurança completamente consistentes, o servidor deve ser parado durante a criação da cópia de segurança.'; +$_ADDONLANG['Backups']['Date'] = 'Data'; +$_ADDONLANG['Backups']['Size'] = 'Tamanho'; +$_ADDONLANG['Backups']['Type'] = 'Tipo'; +$_ADDONLANG['Backups']['Status'] = 'Estado'; +$_ADDONLANG['Backups']['Actions'] = 'Acções'; +$_ADDONLANG['Backups']['Create'] = 'Cópia Agora'; +$_ADDONLANG['Backups']['Available'] = 'Disponível'; +$_ADDONLANG['Backups']['Creating'] = 'A criar...'; +$_ADDONLANG['Backups']['Error'] = 'Erro'; +$_ADDONLANG['Backups']['Automatic'] = 'Automático'; +$_ADDONLANG['Backups']['Manual'] = 'Manual'; + +## Settings +$_ADDONLANG['Settings']['Title'] = 'Menu Definições'; +### Hostname +$_ADDONLANG['Settings']['Hostname']['Title'] = 'Nome do Host'; +$_ADDONLANG['Settings']['Hostname']['Description'] = 'Define o nome do host e o rDNS. Crie primeiro o registo A.'; +$_ADDONLANG['Settings']['Hostname']['Submit'] = 'Submeter'; + +### ISO +$_ADDONLANG['Settings']['ISO']['Title'] = 'ISO'; +$_ADDONLANG['Settings']['ISO']['Description'] = 'Se instalar o sistema operativo através da imagem ISO, deve também configurar a interface de rede estaticamente. Não há servidor DHCP em execução.'; +$_ADDONLANG['Settings']['ISO']['Image'] = 'Imagem ISO'; +$_ADDONLANG['Settings']['ISO']['Submit'] = 'Carregar ISO'; +$_ADDONLANG['Settings']['ISO']['Remove'] = 'Ejectar ISO'; + +### Password +$_ADDONLANG['Settings']['Password']['Title'] = 'Palavra-passe'; +$_ADDONLANG['Settings']['Password']['Description'] = 'A palavra-passe de instalação é removida dos nossos sistemas após 72 horas. É obrigatório alterar a palavra-passe no seu primeiro login!'; +$_ADDONLANG['Settings']['Password']['Submit'] = 'Redefinir Palavra-passe'; + +### Reinstall +$_ADDONLANG['Settings']['Reinstall']['Title'] = 'Reinstalar'; +$_ADDONLANG['Settings']['Reinstall']['Description'] = 'Por favor, entenda que ao reinstalar, todos os dados serão apagados do servidor. Esta acção é irreversível!'; +$_ADDONLANG['Settings']['Reinstall']['OS'] = 'Sistema Operativo'; +$_ADDONLANG['Settings']['Reinstall']['Version'] = 'ESCOLHER VERSÃO'; +$_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Reinstalar'; + +### Firewall +$_ADDONLANG['Settings']['Firewall']['Title'] = 'Firewall'; +$_ADDONLANG['Settings']['Firewall']['Description'] = 'As regras são avaliadas de cima para baixo. Por defeito, tudo é permitido. A firewall está disponível apenas na interface pública. Apenas o tráfego de entrada será filtrado pela firewall.'; +$_ADDONLANG['Settings']['Firewall']['Action'] = 'Acção'; +$_ADDONLANG['Settings']['Firewall']['Port'] = 'Porto'; +$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocolo'; +$_ADDONLANG['Settings']['Firewall']['Source'] = 'Origem'; +$_ADDONLANG['Settings']['Firewall']['Note'] = 'Nota'; +$_ADDONLANG['Settings']['Firewall']['Actions'] = 'Acções'; +$_ADDONLANG['Settings']['Firewall']['Accept'] = 'Aceitar'; +$_ADDONLANG['Settings']['Firewall']['Drop'] = 'Descartar'; +$_ADDONLANG['Settings']['Firewall']['PortNumber'] = 'Número do Porto'; +$_ADDONLANG['Settings']['Firewall']['SourceLabel'] = 'Ex: x.x.x.x/xx (opcional)'; +$_ADDONLANG['Settings']['Firewall']['Notes'] = 'Notas (opcional)'; +$_ADDONLANG['Settings']['Firewall']['Warning'] = 'As regras devem ser confirmadas para fazer efeito.'; +$_ADDONLANG['Settings']['Firewall']['Submit'] = 'Confirmar Firewall'; + +### Rescue Mode +$_ADDONLANG['Settings']['Rescue']['Title'] = 'Modo de Recuperação'; +$_ADDONLANG['Settings']['Rescue']['Description'] = 'O modo de recuperação inicia o seu servidor num sistema Linux temporário onde pode aceder aos discos do seu servidor para reparar ou recuperar dados.'; +$_ADDONLANG['Settings']['Rescue']['Status'] = 'Estado'; +$_ADDONLANG['Settings']['Rescue']['Active'] = 'Activo'; +$_ADDONLANG['Settings']['Rescue']['Inactive'] = 'Inactivo'; +$_ADDONLANG['Settings']['Rescue']['Enable'] = 'Activar Modo de Recuperação'; +$_ADDONLANG['Settings']['Rescue']['EnableReboot'] = 'Activar e Reiniciar'; +$_ADDONLANG['Settings']['Rescue']['Disable'] = 'Desactivar Modo de Recuperação'; +$_ADDONLANG['Settings']['Rescue']['ResetRootPassword'] = 'Redefinir Palavra-passe Root'; +$_ADDONLANG['Settings']['Rescue']['Warning'] = 'Após activar o modo de recuperação, deve reiniciar o servidor dentro de 60 minutos para que faça efeito.'; +$_ADDONLANG['Settings']['Rescue']['PasswordNote'] = 'A palavra-passe root será exibida uma vez após activar o modo de recuperação. Certifique-se de a guardar!'; +$_ADDONLANG['Settings']['Rescue']['DisableTitle'] = 'Desactivar Modo de Recuperação'; +$_ADDONLANG['Settings']['Rescue']['DisableDescription'] = 'Se o modo de recuperação estiver actualmente activo, pode desactivá-lo aqui. O servidor inicializará normalmente no próximo reinício.'; +$_ADDONLANG['Settings']['Rescue']['NewRootPassword'] = 'Nova Palavra-passe Root'; +$_ADDONLANG['Settings']['Rescue']['AboutTitle'] = 'Acerca do Modo de Recuperação'; +$_ADDONLANG['Settings']['Rescue']['AboutDescription'] = 'O sistema de recuperação é um ambiente baseado em rede e pode ser usado para corrigir problemas que impedem um arranque regular. Também é útil para instalar distribuições Linux personalizadas que não são oferecidas directamente por nós. Pode montar o disco rígido do servidor dentro do sistema de recuperação.'; +$_ADDONLANG['Settings']['Rescue']['Important'] = 'Importante'; +$_ADDONLANG['Settings']['Rescue']['ImportantNote'] = 'Após activar o sistema de recuperação, deve reiniciar o servidor nos próximos 60 minutos para o activar. Após outro reinício, o seu servidor inicializará novamente a partir do seu disco local.'; +$_ADDONLANG['Settings']['Rescue']['EnableTitle'] = 'Activar Modo de Recuperação'; +$_ADDONLANG['Settings']['Rescue']['EnableDescription'] = 'Inicializar num sistema Linux minimal para tarefas de recuperação e manutenção.'; +$_ADDONLANG['Settings']['Rescue']['EnableRebootTitle'] = 'Activar e Reiniciar'; +$_ADDONLANG['Settings']['Rescue']['EnableRebootDescription'] = 'Activar modo de recuperação e reiniciar imediatamente o servidor para o activar.'; +$_ADDONLANG['Settings']['Rescue']['PasswordPrompt'] = 'A sua nova palavra-passe root é:'; + +### Floating IP +$_ADDONLANG['Settings']['FloatingIP']['Title'] = 'IP Flutuante'; +$_ADDONLANG['Settings']['FloatingIP']['Description'] = 'Gerir IPs flutuantes atribuídos a este servidor. Os IPs flutuantes podem ser movidos entre servidores.'; +$_ADDONLANG['Settings']['FloatingIP']['Status'] = 'Estado'; +$_ADDONLANG['Settings']['FloatingIP']['IP'] = 'Endereço IP'; +$_ADDONLANG['Settings']['FloatingIP']['Type'] = 'Tipo'; +$_ADDONLANG['Settings']['FloatingIP']['None'] = 'Nenhum IP flutuante atribuído'; +$_ADDONLANG['Settings']['FloatingIP']['NotAvailable'] = 'IP flutuante não disponível para este serviço'; +$_ADDONLANG['Settings']['FloatingIP']['Assigned'] = 'Atribuído'; +$_ADDONLANG['Settings']['FloatingIP']['Unassigned'] = 'Não atribuído'; +$_ADDONLANG['Settings']['FloatingIP']['Assign'] = 'Atribuir ao Servidor'; +$_ADDONLANG['Settings']['FloatingIP']['Unassign'] = 'Desatribuir do Servidor'; +$_ADDONLANG['Settings']['FloatingIP']['ReverseDNS'] = 'DNS Reverso'; +$_ADDONLANG['Settings']['FloatingIP']['ReverseDNSDescription'] = 'Definir DNS reverso (registo PTR) para este IP flutuante'; +$_ADDONLANG['Settings']['FloatingIP']['SetReverseDNS'] = 'Definir DNS Reverso'; +$_ADDONLANG['Settings']['FloatingIP']['ReverseDNSPlaceholder'] = 'exemplo.com'; + +# Additional strings for UI elements +$_ADDONLANG['General']['NoBackupsFound'] = 'Nenhuma cópia de segurança encontrada'; +$_ADDONLANG['General']['NoFirewallRules'] = 'Nenhuma regra de firewall configurada'; +$_ADDONLANG['General']['NoDataAvailable'] = 'Nenhum dado disponível'; +$_ADDONLANG['General']['RefreshAll'] = 'Actualizar Tudo'; +$_ADDONLANG['General']['AddRule'] = 'Adicionar Regra'; +$_ADDONLANG['General']['Note'] = 'Nota'; +$_ADDONLANG['General']['GB'] = 'GB'; +$_ADDONLANG['General']['TB'] = 'TB'; +$_ADDONLANG['General']['Any'] = 'Qualquer'; +$_ADDONLANG['General']['EmptyValue'] = '-'; +$_ADDONLANG['General']['Colon'] = ': '; + +## Firewall specific +$_ADDONLANG['Firewall']['CurrentRules'] = 'Regras de Firewall Actuais'; +$_ADDONLANG['Firewall']['ResourcesAttached'] = 'Os recursos de firewall estão anexados aos servidores. Se nenhuma firewall estiver anexada, uma será criada automaticamente quando adicionar a sua primeira regra.'; +$_ADDONLANG['Firewall']['ChangesImmediate'] = 'As alterações da firewall são aplicadas imediatamente. Não há necessidade de confirmar alterações.'; +$_ADDONLANG['Firewall']['AddNewRule'] = 'Adicionar Nova Regra de Firewall'; +$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535'; +$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0'; +$_ADDONLANG['Firewall']['DescriptionPlaceholder'] = 'Descrição da regra'; +$_ADDONLANG['Firewall']['Accept'] = 'ACEITAR'; +$_ADDONLANG['Firewall']['Drop'] = 'DESCARTAR'; +$_ADDONLANG['Firewall']['Info'] = 'INFO'; +$_ADDONLANG['Firewall']['Any'] = 'QUALQUER'; +$_ADDONLANG['Firewall']['TCP'] = 'TCP'; +$_ADDONLANG['Firewall']['UDP'] = 'UDP'; +$_ADDONLANG['Firewall']['ICMP'] = 'ICMP'; + +## Reinstall specific +$_ADDONLANG['Reinstall']['DestroyWarning'] = 'A reconstrução irá destruir todos os dados no servidor. Uma nova palavra-passe root será gerada e guardada na sua conta de serviço.'; + +## Additional UI messages +$_ADDONLANG['Messages']['PortRequired'] = 'Porto é obrigatório para protocolos TCP/UDP'; +$_ADDONLANG['Messages']['RefreshingGraphs'] = 'A actualizar todos os gráficos...'; +$_ADDONLANG['Messages']['PasswordSaveNote'] = 'Guarde esta palavra-passe num local seguro'; +$_ADDONLANG['Messages']['SelectOSFirst'] = 'Por favor, seleccione primeiro um sistema operativo.'; + +## Server types +$_ADDONLANG['ServerTypes']['Standard'] = 'Padrão'; \ No newline at end of file diff --git a/modules/servers/ArkHostHetznerVPS/lang/russian.php b/modules/servers/ArkHostHetznerVPS/lang/russian.php new file mode 100644 index 0000000..c293281 --- /dev/null +++ b/modules/servers/ArkHostHetznerVPS/lang/russian.php @@ -0,0 +1,231 @@ +** Автоматические резервные копии также заменят ваши ручные резервные копии, если автоматические резервные копии не отключены.
*** Автоматические резервные копии создаются 2 раза в неделю и являются частью нашего плана аварийного восстановления. Если вы отключите автоматические резервные копии, вы также отключите любую возможность восстановления в случае катастрофы.
**** Файловая система резервной копии может быть не полностью согласованной, если VPS записывал в файловую систему в момент создания резервной копии. Для полностью согласованных резервных копий сервер должен быть остановлен во время создания резервной копии.'; +$_ADDONLANG['Backups']['Date'] = 'Дата'; +$_ADDONLANG['Backups']['Size'] = 'Размер'; +$_ADDONLANG['Backups']['Type'] = 'Тип'; +$_ADDONLANG['Backups']['Status'] = 'Статус'; +$_ADDONLANG['Backups']['Actions'] = 'Действия'; +$_ADDONLANG['Backups']['Create'] = 'Создать сейчас'; +$_ADDONLANG['Backups']['Available'] = 'Доступно'; +$_ADDONLANG['Backups']['Creating'] = 'Создается...'; +$_ADDONLANG['Backups']['Error'] = 'Ошибка'; +$_ADDONLANG['Backups']['Automatic'] = 'Автоматический'; +$_ADDONLANG['Backups']['Manual'] = 'Ручной'; + +## Settings +$_ADDONLANG['Settings']['Title'] = 'Меню настроек'; +### Hostname +$_ADDONLANG['Settings']['Hostname']['Title'] = 'Имя хоста'; +$_ADDONLANG['Settings']['Hostname']['Description'] = 'Устанавливает имя хоста и обратную DNS запись. Сначала создайте A запись.'; +$_ADDONLANG['Settings']['Hostname']['Submit'] = 'Отправить'; + +### ISO +$_ADDONLANG['Settings']['ISO']['Title'] = 'ISO'; +$_ADDONLANG['Settings']['ISO']['Description'] = 'Если вы устанавливаете операционную систему через ISO образ, вы также должны статически настроить сетевой интерфейс. DHCP сервер не запущен.'; +$_ADDONLANG['Settings']['ISO']['Image'] = 'ISO образ'; +$_ADDONLANG['Settings']['ISO']['Submit'] = 'Загрузить ISO'; +$_ADDONLANG['Settings']['ISO']['Remove'] = 'Извлечь ISO'; + +### Password +$_ADDONLANG['Settings']['Password']['Title'] = 'Пароль'; +$_ADDONLANG['Settings']['Password']['Description'] = 'Пароль установки удаляется из наших систем через 72 часа. Обязательно измените пароль при первом входе!'; +$_ADDONLANG['Settings']['Password']['Submit'] = 'Сбросить пароль'; + +### Reinstall +$_ADDONLANG['Settings']['Reinstall']['Title'] = 'Переустановка'; +$_ADDONLANG['Settings']['Reinstall']['Description'] = 'Пожалуйста, поймите, что при переустановке все данные будут стерты с сервера. Это действие необратимо!'; +$_ADDONLANG['Settings']['Reinstall']['OS'] = 'Операционная система'; +$_ADDONLANG['Settings']['Reinstall']['Version'] = 'ВЫБЕРИТЕ ВЕРСИЮ'; +$_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Переустановить'; + +### Firewall +$_ADDONLANG['Settings']['Firewall']['Title'] = 'Файервол'; +$_ADDONLANG['Settings']['Firewall']['Description'] = 'Правила оцениваются сверху вниз. По умолчанию все разрешено. Файервол доступен только на публичном интерфейсе. Файервол фильтрует только входящий трафик.'; +$_ADDONLANG['Settings']['Firewall']['Action'] = 'Действие'; +$_ADDONLANG['Settings']['Firewall']['Port'] = 'Порт'; +$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Протокол'; +$_ADDONLANG['Settings']['Firewall']['Source'] = 'Источник'; +$_ADDONLANG['Settings']['Firewall']['Note'] = 'Примечание'; +$_ADDONLANG['Settings']['Firewall']['Actions'] = 'Действия'; +$_ADDONLANG['Settings']['Firewall']['Accept'] = 'Принять'; +$_ADDONLANG['Settings']['Firewall']['Drop'] = 'Отбросить'; +$_ADDONLANG['Settings']['Firewall']['PortNumber'] = 'Номер порта'; +$_ADDONLANG['Settings']['Firewall']['SourceLabel'] = 'Например: x.x.x.x/xx (опционально)'; +$_ADDONLANG['Settings']['Firewall']['Notes'] = 'Примечания (опционально)'; +$_ADDONLANG['Settings']['Firewall']['Warning'] = 'Правила должны быть применены, чтобы вступить в силу.'; +$_ADDONLANG['Settings']['Firewall']['Submit'] = 'Применить файервол'; + +### Rescue Mode +$_ADDONLANG['Settings']['Rescue']['Title'] = 'Режим восстановления'; +$_ADDONLANG['Settings']['Rescue']['Description'] = 'Режим восстановления загружает ваш сервер во временную Linux систему, где вы можете получить доступ к дискам вашего сервера для восстановления или ремонта данных.'; +$_ADDONLANG['Settings']['Rescue']['Status'] = 'Статус'; +$_ADDONLANG['Settings']['Rescue']['Active'] = 'Активен'; +$_ADDONLANG['Settings']['Rescue']['Inactive'] = 'Неактивен'; +$_ADDONLANG['Settings']['Rescue']['Enable'] = 'Включить режим восстановления'; +$_ADDONLANG['Settings']['Rescue']['EnableReboot'] = 'Включить и перезагрузить'; +$_ADDONLANG['Settings']['Rescue']['Disable'] = 'Отключить режим восстановления'; +$_ADDONLANG['Settings']['Rescue']['ResetRootPassword'] = 'Сбросить root пароль'; +$_ADDONLANG['Settings']['Rescue']['Warning'] = 'После включения режима восстановления вы должны перезагрузить сервер в течение 60 минут, чтобы он вступил в силу.'; +$_ADDONLANG['Settings']['Rescue']['PasswordNote'] = 'Root пароль будет показан один раз после включения режима восстановления. Обязательно сохраните его!'; +$_ADDONLANG['Settings']['Rescue']['DisableTitle'] = 'Отключить режим восстановления'; +$_ADDONLANG['Settings']['Rescue']['DisableDescription'] = 'Если режим восстановления в настоящее время активен, вы можете отключить его здесь. Сервер будет загружаться нормально при следующей перезагрузке.'; +$_ADDONLANG['Settings']['Rescue']['NewRootPassword'] = 'Новый root пароль'; +$_ADDONLANG['Settings']['Rescue']['AboutTitle'] = 'О режиме восстановления'; +$_ADDONLANG['Settings']['Rescue']['AboutDescription'] = 'Система восстановления - это сетевая среда, которая может использоваться для решения проблем, препятствующих нормальной загрузке. Она также полезна для установки пользовательских дистрибутивов Linux, которые мы не предлагаем напрямую. Вы можете смонтировать жесткий диск сервера внутри системы восстановления.'; +$_ADDONLANG['Settings']['Rescue']['Important'] = 'Важно'; +$_ADDONLANG['Settings']['Rescue']['ImportantNote'] = 'После включения системы восстановления вы должны перезагрузить сервер в течение следующих 60 минут, чтобы активировать её. После ещё одной перезагрузки ваш сервер снова будет загружаться с локального диска.'; +$_ADDONLANG['Settings']['Rescue']['EnableTitle'] = 'Включить режим восстановления'; +$_ADDONLANG['Settings']['Rescue']['EnableDescription'] = 'Загрузиться в минимальную Linux систему для задач восстановления и обслуживания.'; +$_ADDONLANG['Settings']['Rescue']['EnableRebootTitle'] = 'Включить и перезагрузить'; +$_ADDONLANG['Settings']['Rescue']['EnableRebootDescription'] = 'Включить режим восстановления и немедленно перезагрузить сервер для его активации.'; +$_ADDONLANG['Settings']['Rescue']['PasswordPrompt'] = 'Ваш новый root пароль:'; + +# Additional strings for UI elements +$_ADDONLANG['General']['NoBackupsFound'] = 'Резервные копии не найдены'; +$_ADDONLANG['General']['NoFirewallRules'] = 'Правила файервола не настроены'; +$_ADDONLANG['General']['NoDataAvailable'] = 'Данные недоступны'; +$_ADDONLANG['General']['RefreshAll'] = 'Обновить всё'; +$_ADDONLANG['General']['AddRule'] = 'Добавить правило'; +$_ADDONLANG['General']['Note'] = 'Примечание'; +$_ADDONLANG['General']['GB'] = 'ГБ'; +$_ADDONLANG['General']['TB'] = 'ТБ'; +$_ADDONLANG['General']['Any'] = 'Любой'; +$_ADDONLANG['General']['EmptyValue'] = '-'; +$_ADDONLANG['General']['Colon'] = ': '; + +## Firewall specific +$_ADDONLANG['Firewall']['CurrentRules'] = 'Текущие правила файервола'; +$_ADDONLANG['Firewall']['ResourcesAttached'] = 'Ресурсы файервола прикреплены к серверам. Если файервол не прикреплен, он будет создан автоматически, когда вы добавите первое правило.'; +$_ADDONLANG['Firewall']['ChangesImmediate'] = 'Изменения файервола применяются немедленно. Нет необходимости подтверждать изменения.'; +$_ADDONLANG['Firewall']['AddNewRule'] = 'Добавить новое правило файервола'; +$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535'; +$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0'; +$_ADDONLANG['Firewall']['DescriptionPlaceholder'] = 'Описание правила'; +$_ADDONLANG['Firewall']['Accept'] = 'ПРИНЯТЬ'; +$_ADDONLANG['Firewall']['Drop'] = 'ОТБРОСИТЬ'; +$_ADDONLANG['Firewall']['Info'] = 'ИНФО'; +$_ADDONLANG['Firewall']['Any'] = 'ЛЮБОЙ'; +$_ADDONLANG['Firewall']['TCP'] = 'TCP'; +$_ADDONLANG['Firewall']['UDP'] = 'UDP'; +$_ADDONLANG['Firewall']['ICMP'] = 'ICMP'; + +## Reinstall specific +$_ADDONLANG['Reinstall']['DestroyWarning'] = 'Перестроение уничтожит все данные на сервере. Новый root пароль будет сгенерирован и сохранен в вашей учетной записи службы.'; + +## Additional UI messages +$_ADDONLANG['Messages']['PortRequired'] = 'Порт обязателен для протоколов TCP/UDP'; +$_ADDONLANG['Messages']['RefreshingGraphs'] = 'Обновление всех графиков...'; +$_ADDONLANG['Messages']['PasswordSaveNote'] = 'Сохраните этот пароль в безопасном месте'; +$_ADDONLANG['Messages']['SelectOSFirst'] = 'Пожалуйста, сначала выберите операционную систему.'; + +## Server types +$_ADDONLANG['ServerTypes']['Standard'] = 'Стандартный'; \ No newline at end of file diff --git a/modules/servers/ArkHostHetznerVPS/lang/spanish.php b/modules/servers/ArkHostHetznerVPS/lang/spanish.php new file mode 100644 index 0000000..c337e38 --- /dev/null +++ b/modules/servers/ArkHostHetznerVPS/lang/spanish.php @@ -0,0 +1,231 @@ +** Los respaldos automáticos también reemplazarán tus respaldos manuales a menos que los respaldos automáticos estén desactivados.
*** Los respaldos automáticos se realizan 2 veces por semana y son parte de nuestro plan de recuperación ante desastres. Si desactivas los respaldos automáticos, también desactivas cualquier posibilidad de recuperación en caso de desastre.
**** El sistema de archivos del respaldo podría no estar completamente consistente si el VPS estaba escribiendo al sistema de archivos en el momento del respaldo. Para respaldos completamente consistentes, el servidor debe estar detenido mientras se crea el respaldo.'; +$_ADDONLANG['Backups']['Date'] = 'Fecha'; +$_ADDONLANG['Backups']['Size'] = 'Tamaño'; +$_ADDONLANG['Backups']['Type'] = 'Tipo'; +$_ADDONLANG['Backups']['Status'] = 'Estado'; +$_ADDONLANG['Backups']['Actions'] = 'Acciones'; +$_ADDONLANG['Backups']['Create'] = 'Respaldar Ahora'; +$_ADDONLANG['Backups']['Available'] = 'Disponible'; +$_ADDONLANG['Backups']['Creating'] = 'Creando...'; +$_ADDONLANG['Backups']['Error'] = 'Error'; +$_ADDONLANG['Backups']['Automatic'] = 'Automático'; +$_ADDONLANG['Backups']['Manual'] = 'Manual'; + +## Settings +$_ADDONLANG['Settings']['Title'] = 'Menú de Configuración'; +### Hostname +$_ADDONLANG['Settings']['Hostname']['Title'] = 'Nombre del Host'; +$_ADDONLANG['Settings']['Hostname']['Description'] = 'Establece el nombre del host y el rDNS. Por favor, crea primero el registro A.'; +$_ADDONLANG['Settings']['Hostname']['Submit'] = 'Enviar'; + +### ISO +$_ADDONLANG['Settings']['ISO']['Title'] = 'ISO'; +$_ADDONLANG['Settings']['ISO']['Description'] = 'Si instalas el sistema operativo a través de la imagen ISO, también debes configurar la interfaz de red de forma estática. No hay servidor DHCP ejecutándose.'; +$_ADDONLANG['Settings']['ISO']['Image'] = 'Imagen ISO'; +$_ADDONLANG['Settings']['ISO']['Submit'] = 'Cargar ISO'; +$_ADDONLANG['Settings']['ISO']['Remove'] = 'Expulsar ISO'; + +### Password +$_ADDONLANG['Settings']['Password']['Title'] = 'Contraseña'; +$_ADDONLANG['Settings']['Password']['Description'] = 'La contraseña de instalación se elimina de nuestros sistemas después de 72 horas. ¡Es obligatorio cambiar la contraseña en tu primer inicio de sesión!'; +$_ADDONLANG['Settings']['Password']['Submit'] = 'Restablecer Contraseña'; + +### Reinstall +$_ADDONLANG['Settings']['Reinstall']['Title'] = 'Reinstalar'; +$_ADDONLANG['Settings']['Reinstall']['Description'] = '¡Por favor entiende que al reinstalar, todos los datos serán eliminados del servidor. Esta acción es irreversible!'; +$_ADDONLANG['Settings']['Reinstall']['OS'] = 'Sistema Operativo'; +$_ADDONLANG['Settings']['Reinstall']['Version'] = 'ELEGIR VERSIÓN'; +$_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Reinstalar'; + +### Firewall +$_ADDONLANG['Settings']['Firewall']['Title'] = 'Firewall'; +$_ADDONLANG['Settings']['Firewall']['Description'] = 'Las reglas se evalúan de arriba hacia abajo. Por defecto, todo está permitido. El firewall solo está disponible en la interfaz pública. Solo el tráfico entrante será filtrado por el firewall.'; +$_ADDONLANG['Settings']['Firewall']['Action'] = 'Acción'; +$_ADDONLANG['Settings']['Firewall']['Port'] = 'Puerto'; +$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocolo'; +$_ADDONLANG['Settings']['Firewall']['Source'] = 'Origen'; +$_ADDONLANG['Settings']['Firewall']['Note'] = 'Nota'; +$_ADDONLANG['Settings']['Firewall']['Actions'] = 'Acciones'; +$_ADDONLANG['Settings']['Firewall']['Accept'] = 'Aceptar'; +$_ADDONLANG['Settings']['Firewall']['Drop'] = 'Descartar'; +$_ADDONLANG['Settings']['Firewall']['PortNumber'] = 'Número de Puerto'; +$_ADDONLANG['Settings']['Firewall']['SourceLabel'] = 'Ej: x.x.x.x/xx (opcional)'; +$_ADDONLANG['Settings']['Firewall']['Notes'] = 'Notas (opcional)'; +$_ADDONLANG['Settings']['Firewall']['Warning'] = 'Las reglas deben ser confirmadas para tomar efecto.'; +$_ADDONLANG['Settings']['Firewall']['Submit'] = 'Confirmar Firewall'; + +### Rescue Mode +$_ADDONLANG['Settings']['Rescue']['Title'] = 'Modo de Rescate'; +$_ADDONLANG['Settings']['Rescue']['Description'] = 'El modo de rescate arranca tu servidor en un sistema Linux temporal donde puedes acceder a los discos de tu servidor para reparar o recuperar datos.'; +$_ADDONLANG['Settings']['Rescue']['Status'] = 'Estado'; +$_ADDONLANG['Settings']['Rescue']['Active'] = 'Activo'; +$_ADDONLANG['Settings']['Rescue']['Inactive'] = 'Inactivo'; +$_ADDONLANG['Settings']['Rescue']['Enable'] = 'Activar Modo de Rescate'; +$_ADDONLANG['Settings']['Rescue']['EnableReboot'] = 'Activar y Reiniciar'; +$_ADDONLANG['Settings']['Rescue']['Disable'] = 'Desactivar Modo de Rescate'; +$_ADDONLANG['Settings']['Rescue']['ResetRootPassword'] = 'Restablecer Contraseña Root'; +$_ADDONLANG['Settings']['Rescue']['Warning'] = 'Después de activar el modo de rescate, debes reiniciar el servidor dentro de 60 minutos para que tome efecto.'; +$_ADDONLANG['Settings']['Rescue']['PasswordNote'] = 'La contraseña root se mostrará una vez después de activar el modo de rescate. ¡Asegúrate de guardarla!'; +$_ADDONLANG['Settings']['Rescue']['DisableTitle'] = 'Desactivar Modo de Rescate'; +$_ADDONLANG['Settings']['Rescue']['DisableDescription'] = 'Si el modo de rescate está actualmente activo, puedes desactivarlo aquí. El servidor arrancará normalmente en el próximo reinicio.'; +$_ADDONLANG['Settings']['Rescue']['NewRootPassword'] = 'Nueva Contraseña Root'; +$_ADDONLANG['Settings']['Rescue']['AboutTitle'] = 'Acerca del Modo de Rescate'; +$_ADDONLANG['Settings']['Rescue']['AboutDescription'] = 'El sistema de rescate es un entorno basado en red y puede ser usado para solucionar problemas que impiden un arranque normal. También es útil para instalar distribuciones Linux personalizadas que no ofrecemos directamente. Puedes montar el disco duro del servidor dentro del sistema de rescate.'; +$_ADDONLANG['Settings']['Rescue']['Important'] = 'Importante'; +$_ADDONLANG['Settings']['Rescue']['ImportantNote'] = 'Después de activar el sistema de rescate necesitas reiniciar el servidor en los próximos 60 minutos para activarlo. Después de otro reinicio tu servidor arrancará desde su disco local nuevamente.'; +$_ADDONLANG['Settings']['Rescue']['EnableTitle'] = 'Activar Modo de Rescate'; +$_ADDONLANG['Settings']['Rescue']['EnableDescription'] = 'Arrancar en un sistema Linux mínimo para tareas de recuperación y mantenimiento.'; +$_ADDONLANG['Settings']['Rescue']['EnableRebootTitle'] = 'Activar y Reiniciar'; +$_ADDONLANG['Settings']['Rescue']['EnableRebootDescription'] = 'Activar modo de rescate e inmediatamente reiniciar el servidor para activarlo.'; +$_ADDONLANG['Settings']['Rescue']['PasswordPrompt'] = 'Tu nueva contraseña root es:'; + +# Additional strings for UI elements +$_ADDONLANG['General']['NoBackupsFound'] = 'No se encontraron respaldos'; +$_ADDONLANG['General']['NoFirewallRules'] = 'No hay reglas de firewall configuradas'; +$_ADDONLANG['General']['NoDataAvailable'] = 'No hay datos disponibles'; +$_ADDONLANG['General']['RefreshAll'] = 'Actualizar Todo'; +$_ADDONLANG['General']['AddRule'] = 'Agregar Regla'; +$_ADDONLANG['General']['Note'] = 'Nota'; +$_ADDONLANG['General']['GB'] = 'GB'; +$_ADDONLANG['General']['TB'] = 'TB'; +$_ADDONLANG['General']['Any'] = 'Cualquiera'; +$_ADDONLANG['General']['EmptyValue'] = '-'; +$_ADDONLANG['General']['Colon'] = ': '; + +## Firewall specific +$_ADDONLANG['Firewall']['CurrentRules'] = 'Reglas Actuales del Firewall'; +$_ADDONLANG['Firewall']['ResourcesAttached'] = 'Los recursos del firewall están adjuntos a los servidores. Si no hay firewall adjunto, se creará uno automáticamente cuando agregues tu primera regla.'; +$_ADDONLANG['Firewall']['ChangesImmediate'] = 'Los cambios del firewall se aplican inmediatamente. No es necesario confirmar cambios.'; +$_ADDONLANG['Firewall']['AddNewRule'] = 'Agregar Nueva Regla del Firewall'; +$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535'; +$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0'; +$_ADDONLANG['Firewall']['DescriptionPlaceholder'] = 'Descripción de la regla'; +$_ADDONLANG['Firewall']['Accept'] = 'ACEPTAR'; +$_ADDONLANG['Firewall']['Drop'] = 'DESCARTAR'; +$_ADDONLANG['Firewall']['Info'] = 'INFO'; +$_ADDONLANG['Firewall']['Any'] = 'CUALQUIERA'; +$_ADDONLANG['Firewall']['TCP'] = 'TCP'; +$_ADDONLANG['Firewall']['UDP'] = 'UDP'; +$_ADDONLANG['Firewall']['ICMP'] = 'ICMP'; + +## Reinstall specific +$_ADDONLANG['Reinstall']['DestroyWarning'] = 'La reinstalación destruirá todos los datos en el servidor. Se generará una nueva contraseña root y se guardará en tu cuenta de servicio.'; + +## Additional UI messages +$_ADDONLANG['Messages']['PortRequired'] = 'El puerto es requerido para protocolos TCP/UDP'; +$_ADDONLANG['Messages']['RefreshingGraphs'] = 'Actualizando todos los gráficos...'; +$_ADDONLANG['Messages']['PasswordSaveNote'] = 'Guarda esta contraseña en un lugar seguro'; +$_ADDONLANG['Messages']['SelectOSFirst'] = 'Por favor selecciona primero un sistema operativo.'; + +## Server types +$_ADDONLANG['ServerTypes']['Standard'] = 'Estándar'; \ No newline at end of file diff --git a/modules/servers/ArkHostHetznerVPS/template/clientarea_direct.tpl b/modules/servers/ArkHostHetznerVPS/template/clientarea_direct.tpl new file mode 100644 index 0000000..ff651a8 --- /dev/null +++ b/modules/servers/ArkHostHetznerVPS/template/clientarea_direct.tpl @@ -0,0 +1,756 @@ +{* + * WHMCS Server Module - Hetzner VPS + * @package WHMCS + * @copyright ArkHost 2025 + * @link https://arkhost.com + * @author ArkHost + *} + + + + + + + +
+ + +
+
+ +
+
+ +
+
+
{$ADDONLANG.Overview.ServerInfo}
+
+
+
+
+ + + + + + + + + + {if $serverInfo['ipv6']} + + + + + {/if} + + + + +
{$ADDONLANG.Overview.Hostname}:{$serverInfo['hostname']}
{$ADDONLANG.IPv4} Address:{$serverInfo['ip']}
{$ADDONLANG.IPv6} Address:{$serverInfo['ipv6']}
{$ADDONLANG.Overview.OS}: + + {$serverInfo['operatingSystem']['name']} +
+
+
+ + + + + + + + + + + + + + + + + +
{$ADDONLANG.Overview.Status}: + + + {$serverInfo['statusDescription']} + +
{$ADDONLANG.Overview.Uptime}:{$serverInfo['uptime_text']}
{$ADDONLANG.Overview.Location}:{$serverInfo['datacenter']}
{$ADDONLANG.Overview.ServerType}:{$serverInfo['server_type']['description']|default:$ADDONLANG.ServerTypes.Standard}
+
+
+
+
+ + +
+
+
{$ADDONLANG.Overview.ResourceAllocation}
+
+
+
+
+
+ +

{$serverInfo['cpu']}

+ {$ADDONLANG.Overview.CPU_Cores} +
+
+
+
+ +

{$serverInfo['ram']} {$ADDONLANG.General.GB}

+ {$ADDONLANG.Overview.Memory} +
+
+
+
+ +

{$serverInfo['disk']} {$ADDONLANG.General.GB}

+ {$ADDONLANG.Overview.Storage} +
+
+
+
+ +

20 {$ADDONLANG.General.TB}

+ {$ADDONLANG.Overview.Traffic} +
+
+
+
+
+ + + +
+
+
{$ADDONLANG.Overview.QuickActions}
+
+
+
+ + + {if $serverInfo['status'] == 'running'} + + {/if} + + {$ADDONLANG.VNC} + +
+
+
+
+ +
+ +
+
+
{$ADDONLANG.Graphs.Title}
+ +
+
+

{$ADDONLANG.Graphs.SelectPeriod}

+
+ + + + + +
+
+
+ + + + + +
+
+
+
+
{$ADDONLANG.Graphs.CPU}
+
+
+
+

{$ADDONLANG.Graphs.Loading}

+
+
+
+
+ +
+
+
+
{$ADDONLANG.Graphs.Network}
+
+
+
+

{$ADDONLANG.Graphs.Loading}

+
+
+
+
+ +
+
+
+
{$ADDONLANG.Graphs.Disk}
+
+
+
+

{$ADDONLANG.Graphs.Loading}

+
+
+
+
+
+ +
+ + {if $backupsEnabled} +
+ +
+
+
{$ADDONLANG.Backups.Title}
+
+
+

{$ADDONLANG.Backups.Description}

+ +
+ +
+ +
+ + + + + + + + + + + + + + + +
{$ADDONLANG.Backups.Date} {$ADDONLANG.Backups.Size} {$ADDONLANG.Backups.Type} {$ADDONLANG.Backups.Status} {$ADDONLANG.Backups.Actions}
+ {$ADDONLANG.LoadingBackups} +
+
+
+
+ + +
+
Important Information
+ {$ADDONLANG.Backups.Warning|unescape:'html'} +
+
+ {/if} + +
+
+ + +
+
+
+
+
+
{$ADDONLANG.Settings.Hostname.Title}
+
+
+
+ {$ADDONLANG.Settings.Hostname.Description} +
+ +
+ + +
+ +
+ +
+
+
+
+ +
+
+
+
{$ADDONLANG.Settings.ISO.Title}
+
+
+
+ {$ADDONLANG.Settings.ISO.Description} +
+ +
+ + +
+ +
+ + + {if $serverInfo['iso'] !== ''} + + {/if} +
+
+
+
+ +
+
+
+
{$ADDONLANG.Settings.Password.Title}
+
+
+
+ {$ADDONLANG.Settings.Password.Description} +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
{$ADDONLANG.Settings.Reinstall.Title}
+
+
+
+ {$ADDONLANG.Settings.Reinstall.Description} +
+ +
{$ADDONLANG.Settings.Reinstall.OS}:
+ +
+ {foreach from=$operatingSystems key=$group item=$operatingSystemsGroup} +
+ +
+ {/foreach} +
+ +
+ {$ADDONLANG.General.Note}: {$ADDONLANG.Reinstall.DestroyWarning} +
+ + + +
+ +
+
+
+
+ +
+
+
+
{$ADDONLANG.Settings.Rescue.Title}
+
+
+
+
{$ADDONLANG.Settings.Rescue.AboutTitle}
+

{$ADDONLANG.Settings.Rescue.AboutDescription}

+
+

{$ADDONLANG.Settings.Rescue.Important}: {$ADDONLANG.Settings.Rescue.ImportantNote}

+
+ +
+
+
+
+ +
{$ADDONLANG.Settings.Rescue.EnableTitle}
+

{$ADDONLANG.Settings.Rescue.EnableDescription}

+ +
+
+
+
+
+
+ +
{$ADDONLANG.Settings.Rescue.EnableRebootTitle}
+

{$ADDONLANG.Settings.Rescue.EnableRebootDescription}

+ +
+
+
+
+ +
+
+
{$ADDONLANG.Settings.Rescue.DisableTitle}
+

{$ADDONLANG.Settings.Rescue.DisableDescription}

+ +
+
+
+
+
+ +
+
+
+
{$ADDONLANG.Settings.FloatingIP.Title}
+
+
+
+ {$ADDONLANG.Settings.FloatingIP.Description} +
+ +
+
+ +

Loading floating IP information...

+
+
+
+
+
+
+
+
+
+ +
+
+ {$ADDONLANG.General.Note}: {$ADDONLANG.Firewall.ResourcesAttached} +
+ + +
+
+
{$ADDONLANG.Firewall.AddNewRule}
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
{$ADDONLANG.Firewall.CurrentRules}
+
+
+
+ + + + + + + + + + + + + + + + +
ActionProtocolPortSourceDescriptionRemove
+ {$ADDONLANG.LoadingFirewallRules} +
+
+
+
+ + +
+ {$ADDONLANG.General.Note}: {$ADDONLANG.Firewall.ChangesImmediate} +
+
+
+
+
+
+ + diff --git a/modules/servers/ArkHostHetznerVPS/template/img/cloud.png b/modules/servers/ArkHostHetznerVPS/template/img/cloud.png new file mode 100644 index 0000000000000000000000000000000000000000..fd805bbefb0546b82b07a31d25ee4d7ac2171b93 GIT binary patch literal 3395 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 literal 0 HcmV?d00001 diff --git a/modules/servers/ArkHostHetznerVPS/template/img/installing.png b/modules/servers/ArkHostHetznerVPS/template/img/installing.png new file mode 100644 index 0000000000000000000000000000000000000000..14adb20332b6fa002a16570627ba44d5226ba81f GIT binary patch literal 3968 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 literal 0 HcmV?d00001 diff --git a/modules/servers/ArkHostHetznerVPS/template/img/lock.png b/modules/servers/ArkHostHetznerVPS/template/img/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..06025ed47cbb84614eed566cda19a13259e3c9ac GIT binary patch literal 3443 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 VjE|n!dqn^M002ovPDHLkV1gM|W3B)I literal 0 HcmV?d00001 diff --git a/modules/servers/ArkHostHetznerVPS/template/img/notice.png b/modules/servers/ArkHostHetznerVPS/template/img/notice.png new file mode 100644 index 0000000000000000000000000000000000000000..4925a7b51c19031463842312640d4d1fea99bd12 GIT binary patch literal 269 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 literal 0 HcmV?d00001 diff --git a/modules/servers/ArkHostHetznerVPS/template/img/os/fedora.png b/modules/servers/ArkHostHetznerVPS/template/img/os/fedora.png new file mode 100644 index 0000000000000000000000000000000000000000..6966c9ca1ad3430d59d1d12d73d59b9ee31d4d74 GIT binary patch literal 3239 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 Z{{y%s9y&>YPrU#D002ovPDHLkV1fpt9xng@ literal 0 HcmV?d00001 diff --git a/modules/servers/ArkHostHetznerVPS/template/img/os/iso.png b/modules/servers/ArkHostHetznerVPS/template/img/os/iso.png new file mode 100644 index 0000000000000000000000000000000000000000..96a93e760ba2e4f829403351bb1fc018577a0dca GIT binary patch literal 2754 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 IM6N<$f~@l_(*OVf literal 0 HcmV?d00001 diff --git a/modules/servers/ArkHostHetznerVPS/template/img/os/lubuntu.png b/modules/servers/ArkHostHetznerVPS/template/img/os/lubuntu.png new file mode 100644 index 0000000000000000000000000000000000000000..afdfd9fee6fb166ebfdeaab3673b12e217166ca5 GIT binary patch literal 3287 zcmV;|3@G!7P)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 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 VFfbq(ug?Gg002ovPDHLkV1l(GGm`)S literal 0 HcmV?d00001 diff --git a/modules/servers/ArkHostHetznerVPS/template/img/os/mikrotik.png b/modules/servers/ArkHostHetznerVPS/template/img/os/mikrotik.png new file mode 100644 index 0000000000000000000000000000000000000000..dfc5e7b7f43f50f71a65500bc9f3b1f88f0f6cca GIT binary patch literal 3601 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 QBOC#srmO|4cwvG1ABl+B)Bpeg literal 0 HcmV?d00001 diff --git a/modules/servers/ArkHostHetznerVPS/template/img/os/opensuse.png b/modules/servers/ArkHostHetznerVPS/template/img/os/opensuse.png new file mode 100644 index 0000000000000000000000000000000000000000..077f59c6db54d046e2310ddea82e2e34022ff736 GIT binary patch literal 2953 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 literal 0 HcmV?d00001 diff --git a/modules/servers/ArkHostHetznerVPS/template/img/os/rockylinux.png b/modules/servers/ArkHostHetznerVPS/template/img/os/rockylinux.png new file mode 100644 index 0000000000000000000000000000000000000000..99363cac98467373775af416a57f7974ae620503 GIT binary patch literal 2265 zcmV;~2qyQ5P) zOLE&*7KYDZEM*j#y#g~UFtY+vgLZmm?ZDIuVh_8zIwIxj>Y?QoNM3=JndJ-wt-#a@ z%&Z`kGD##`$PXBoBuIz^hzr~Y&R2DaOI5i<@&9mN1ZXCq(ReRF`@{dbL|+~fgUqfMp;e!{B@gdub-p)?GvGEcLNL+bV6G@ZzsDiCwOix)(X)6 z^;M7V!k?0}-C@uxwD`^BpZ^WWLKtNgaqwYyfa;M?FbAliZ6Ywr3cL+|5z9nilu_c@ z{O#?r_rWg`iLkY`Z+gOLOn`WDcXN#~o(9xx2DH6{m4lhdq7ebwXRr2%?p~wWAt7Q+ zt=7JYVS!P#5uf#c{qY*RX?+rw3HgM+{qAL*y+LgPeBH$Pe2iunCEO485FYH;wbfjU z0PPQleN6JAF29Wu)-%B*@#W;{&nHwdjH(Rj@=JmtVe+q^_tB~hMpZ%_{>}9{nq8Ez zIU}qRcB`5fR3dy3Hq5pf?b^wXcIuf zuMMpdXb~V8^FN~5A)yYm%?TJ)iX{B#% z$(20s&=hQ?)!Hp`Wl?SclCJ+FnjI2sW{job_c-Q)QMqWJ4F^PYfD-Iy2nhz1gIof{ zmu}zOd_q%jh3)O{^IUSCPk;}H=UDUiNN@+{0DA+wXsv`%D~a#exw|_@Q*ezO6GBF< zM9zMP$R#mR_kcCM0Hf|j&VCPA(+e;v6FK`mVnr{&s7$c#KgHo0N^qaI*ca$i z2AKjRyMDi;Dfq(n&Ofp~D9RS#{oxCI;PV=6kd_(sO|aE1WGBMRv!G=*YZq9Pun?1bsfd zqlla9fb5lE>7!ysO_5LiHX;sR&9IXdR6w>$urLgy1^95-$Ijm|nnFHO;VZ0`VBrd4 zMomHYe~QC1l(3%6@D&zIklGYl6yW#hfD%@-GJJ)l5~StzQQsIh8PA01ZD!94Fe*2f)bV~6~4kE2`(T& zkHEYDpK$1qu!xrM6_!Xaod_~21_FdQ2x+v2udqP^2oPr ze1#ZWP311hQC1BJ> z6u5#|!q<&P2^h68IpzZrepP6afKjD@1Oa955=!{hGo1W*FrX5Y7vSrg9Z-5wDB)L+ zmiM@{01@$DD#DL&Qh>&UKlf!$RpFcf z%?W?@MNU=XlmHD0{}PkEzV6B1eK;dPL&7h;wObS?1n@rmXmjVARzLdoZmJpU1@Jz6 zO!R1LXHeBFe;rl};Cc9OX?q*Nr@yhIZmbo+5A_h`B3GzWEbETyTGkcCS^*SsZ*cp^9);lxJbjFE0j%Zuh_=2vH8r`*i}eD` z6ZZ=AxlbYW$UYe^QPJ6cYU|mag;N5|6W_3Ud)q_L&?7U$#1#sGbKt_6ejm;Xu*lcm z;Y$}nbcoPHgw(-b_XzFsL>o0sVB2E?V=b=Ofe{ n2)RWQG|0ADyKWdIG?>2uqJPcIuS&EM00000NkvXXu0mjfvB(#D literal 0 HcmV?d00001 diff --git a/modules/servers/ArkHostHetznerVPS/template/img/os/ubuntu.png b/modules/servers/ArkHostHetznerVPS/template/img/os/ubuntu.png new file mode 100644 index 0000000000000000000000000000000000000000..087c1e871986d487898bb7fabb1c59f34ad3cd7d GIT binary patch literal 3351 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 bRxtKIp^Z#xF;)d+00000NkvXXu0mjf7a;n1 literal 0 HcmV?d00001 diff --git a/modules/servers/ArkHostHetznerVPS/template/img/running.png b/modules/servers/ArkHostHetznerVPS/template/img/running.png new file mode 100644 index 0000000000000000000000000000000000000000..2429bccbdf382a7b62d0ad980a13ce85f45cf2d3 GIT binary patch literal 3603 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 Z{{wr4H2oy<7L@=1002ovPDHLkV1nEIv}FJQ literal 0 HcmV?d00001 diff --git a/modules/servers/ArkHostHetznerVPS/template/img/search.png b/modules/servers/ArkHostHetznerVPS/template/img/search.png new file mode 100644 index 0000000000000000000000000000000000000000..b7af958c676242a9cc7262137d3d10b8f256b3b8 GIT binary patch literal 3724 zcmV;74s-E|P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D4k1ZIK~#8NHm?Gu{wqi<+#sh3jq7f~FCk5cuqPgrhiKy)E_ zhh!6X9JcTd&8EQUBGxnKo>!pi??M2xvn7y~_X<|&2@FrDC}`$l@>FK7)e?}|;T@7? zpyTj`B0P@?P+b!Oerw7DzTpdS{$2 z1h@yxm(DYi)=_NQmlQUulsuRaz)=6U1@7k&l+oMv2uzo2x;DqH7=zun_nN*;Mn4%t zFo^&R5)!|Ra#nmtg=;UQuEeT4Xj9EfqvsV(AEomcm zBNNwTEBH)?cB-tQ|&K9t;p#A#uy7 zWOE~_mK@=Bx+sYI7jL{KrOQ>3OmoyL$Mbd?6t|eH$e& zs3sq--)?>>6gq1K%`f~$6h|9#fJ|)KW-*9HW-BGk+WJ zR?hxWi~7ah+XB{uCqNO9!7EcSgm@ZPD*vbgQ3!2V7H!C(4Z=i*6_bPw8UCApWPyy^ z_dbA80x%K85n_HI0R?L=u*)6eepvZQjy*JO!8Nx79&xUP@6_h+?-t^1Fz9kub=N@lmJOOJekyjeN?pW3S0f0SRTqF zETZPF?PYl=flxq#Lmo&kIwSmgFGAFn@l=TVA5t$6F3`k2*eMR}J zYAHBwsa_};Cx8L>2Ahf56_i_gN+tlyL-}8dt)T$C3Q7Pv55pOG00e2;nG)lD|87_Y zef0B`Ndy4!0$of7%vdBhkeD>kcPN3$>lD#IQwIC@8Q3;1ql3u==l~+%5&duQ>qS2T zU{yn%hBLGTGzkIxquGZOkD-pk6@HNm`K&stAwa2~ql{gm*J-!{9*^ZRSGqVMz`{cH zd+%U%H5L}Ar#_*%!xqrbpRLbE-;|!7{yQN+cQ`8&lbsX^52kv@^*DORy&6{VitUD8 ztlu3M-^3`b%0D0Gw$ABeZC&nlevwH=AEFS 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 LNkvXXu0mjf@J`mq literal 0 HcmV?d00001 diff --git a/modules/servers/ArkHostHetznerVPS/template/img/stopped.png b/modules/servers/ArkHostHetznerVPS/template/img/stopped.png new file mode 100644 index 0000000000000000000000000000000000000000..c2ed128c8bd7c0ec1bda618482df07b691a2383b GIT binary patch literal 3513 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 literal 0 HcmV?d00001 diff --git a/modules/servers/ArkHostHetznerVPS/template/img/suspended.png b/modules/servers/ArkHostHetznerVPS/template/img/suspended.png new file mode 100644 index 0000000000000000000000000000000000000000..8a1de3c217db449484135251015f906b4f3899ee GIT binary patch literal 3368 zcmV+@4cGFCP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D482K2K~#8N!-QO{d z6>@xbu1*L zY$=ed${{AT9*~6Z$!;b9zc?0?T89Fq(-bTdr4YNl0c6V3ZYDscEV*OpBR>_9i5eo{ z8z$iAW;`Jxu?h)LUoR>u`UQo6ppXHw*b>;#YhTS!%X*%J~2Xn7wRyT4>oB$4@$Q+ga)U%CmI#r+_T$b-1_W@zsD8p`&*%U(bPlKsL#H&|!k1Yp9WhRE%w6YvrLRix}O zYc8M>CnJdb$RrJSl9MljzK>3+WrIkDaziw}pPow%I!GX4s*Z8KFknssh{=TP103^H z38+1M3ElLsC|4%f_+mf_mO4kFSGMn6&NVa`xfLg&zViyiXYJ=upct>$eg-?rUWMT3 z-RubzF690+@=|=3TYiGWl@d4EWBi^u2{0rbN_f;rb=${0@?<=PMM`c5GcTyS2+%vj z<13-G=?oOrzs*Z?8qI8seIj3(u5*0~1?msw0{WheE@?ai#SMSvwPNgzkl8%?awf!4MsE9ywWN>iyh02AE8M0o0IgnlXOCql-xd{EYVkHvoXQ z!#Be*i%u1?WJD!ZQ=-4byFr<lrJYy^{BIl+ga%ZaE z^%eTZt@o26nO+@>rJ<^a_|h2x&%eQW*;eR@{(9Q zw)ljAbATKiJcFe=0=&eR9eqOug)J3M{$WUffVIvZu+G^Nd}PTWR_s7Sd||DVJ9->q};6buEE2#8MA2ounzI~8^lE6hv)z8Bq^j15m#gDjzl z!;r}E!;p(ZH>dT=ADp%_5pkIfJj!dGkqGo4%2i>B#d;lg_FxJPXMbAsOeSGY0?3^_ zr7EatyU6E&#soAT`2u70xZb_YJ2euaZb8w|)crN*9g2M#RZ7C0Cw_v^_yTx-lOu#D zsX_btE$#<-f-B~a#;z~Mi-XOu8g^4pM{sA8GT1GLS#&M z0Bwdm#b+J;WYdI}A{-E_T|;0!8kiXA3EDgnd{DC*uC=U!?+!l0_}ji!aK7ew=*$&C zJjxImq(i!fcKAHNOMIFMaPSOvr2Vm@-_ku3*o;{39?XVh#Ja;rd!B>QE^C0k<#4Zi z1q>gth9RvDV+23bGy0BXF4V4uv>11I4s|zpNO5YF@p?k!E^%T4=-TMvCwK4)x*+ll zx`Wfm7-r5T0|(E*u@~He$F_J&?wu%F2hg(u?jvq@*ut$jKr$ptc%^6qk|`Z}VXO3< z$iv59Z~DX~Q}(iB z9XPrizHM3a!}e{tFX~O!T|uha$K~;5m#Kh|erBd$T9*n!lp2VLFNZH%>~Z9m!GM5q zrh;^#cNu)$wiUuug4>-o7!J0e*%5!030R6hj>?o} z5FA?ppEhrVv94u;hAalqYmNEf`F}70LLHcl((!sdbJhgShCqb|3Np9D-6Ov>DcjE% zgPnFT+HEsB&|$M`Rsy&tAhv`{&Zs_zepe$R9#oxxt_Gn5e4}gNm9jv9?xzF|FbClH zGf1vo=8Ob{#1yl^RF*b=E^y!iWEJ=8Ji*K)5UEu86`oK9;+S%{*d&5c^nl47$6Rpe zDHu3}NpT*y)kZ{4&LePiCERMaIVDJdZiLIAr12f7>%0P*gSyfbdw;7-SsDey4P3dbB^@Fgb%hJs0Og4;($tZUrXa2JL^yUpKc$N@AOFyB z`p+_|yM560g8oqzuq!I{b`v8%oC&Rri+zjCcM#zwbU=Z87-)jS9*-6)}tf){j zPW>a5r3-c9VZ$%F8ctVv@fOw`+hGE=b-G3j-cdDQum|9ZZ!fLFggWrBK|4bFdj)re z(2Yx40fXiPI#;;VD1w;n%@CxhXkr3n38hb<2Yf>$Pzfphx5iRI6e1+P4wNYs@Oi5v zz%g73FW^Y*zgjdNb+7JsfrRvW2#VEQBD}-&*;0mC^OP+`!+uJ(5qX{HQc-M*u$TcU%8nFFwO%W%WOrf|KXoDnzIN0000MP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D46{i@K~#8N;dvMjIT5PtQ5~a9KZgw~7gDzM0?be&616@BRJm zIp@3g`|f&Jj#zD_TA5PwUaZzQh~L3IVH}K0u6aLJW7L>wn-00g(4xs|0cC2ffRTHF zI1*Qx*3io2Y>J~VAQ z3(FC8>$)H#f76JIKw^`)>3JJCNm2U>osrKIGO)AwJ3*Jz2FW??)^m$ZoYkg^&zT_p zF4PvmsQ{#6R>||QV#_(G?>NoG2@UJdFw~2hL1r@&_X}#TxM3f(@B9)n3O0dQna@Pp zc3yNAqtT^v@;DNJMl`o_4|^r` zPlDI(vPo}*5?@qNw%R3VW!tUAIY;3&!5D=2O$2T;2U3%8(Km#|wK+m1m| z{a&j8g|)9kP5TKZLav{lzmaV+i?*^2d53ZJF(|IzXD0uD3P2z?qKsS7{5FpKDX3j@ z5-Z%xt~HVg&>keLt-$n2cLdY-J7nmZZ5hWlL)&mk00NB&RX_=eh?8f07n30ikvr!@ z-Pr!zbj`7iEnF6WK$Q?_+X=~7#~lH1ZR|_{cLZ^kX^lEs4Te&d6)NP;pw*y4YYg`g z=^B5A7p_p&v@hhYFmbKwQK<|M^FtAZwkjk(wf3?qjV2p4ZVloXpH_bvb6Oaaw;UpQ z-W0JaZ(l@Q&bQ%;#ka!c+3p48MBGHGyuU=MihpgUZ91L{kWBRnk-Y64B)Ty>FcRDu z-hrYUz9G?vo(q#qW%B<1{y*{#5}%zL6$d`S(LV?<*P!TgUS4TG_V5mp%<+$i6)fn^ z@Q+Y{Z?M?k!zWmz_KW-h3V@u^^R*t{!6J<-0$^c=&~IZAB*5%IvCfGHNMP222$(T1 zgvnD9nK3UIzl#LQjxAU{V0Zx}@a+6Bm@i6zg~c18wCOEej=lj&*-ch4?zXufpx6Ns zFmrw=)*S`DArk(i^JfFX`FCoU`b41bRK~H5P&+0#6@W&5zBmagTRWk7+j)KpL=Q$= zwtogoDt2;1)CqgLdj)kkHnkiIYX1yDF)8eIfl?jh*SrdfGy{{Sav4P%AhTpMlO>|^ zWIfvi%Ti4?5-r-i0?PC%haxx?fGW=_+Xl^B&-1eY+b2TJTR*{u%U~upXm59~pw2Hb z?1GIie+98AdU(dy4`jL$*s$j^EUVfLUjBjL7p~x^K+89u!&_NAO!r#=ITbIm4T(fH zQcV%8-}4{HtA3d|2(?9UC;-K{Z>SW?n-B6Maa;0)68KOI7zo(qxy)B#5>cCYaA4}z53 zHrV!hAEf24g=xP25E`2SYhSv=&jpDX4odjW-f%t z(_qc6e?oQp39i@v&gE19YG_WFoHKvRwohRN#vQpn#eZtv8cZ+4ah1|e{n+sqlAW3p_B1_qx+??Y7PCT1#(R7V|Gu{3KY%oNJ^HLWC zFkv<(6-z}F7N!cv{*2HnfU8qscLNE)Q(^C5NrGRbLa>lK!=E?@ijzEiLZiI=#_)1@ zntu#DJx2yJ0;CxG!exX&oV`ONGx(v1Pmp;3Jdu)nAO)IE+K9q1~K`KT6hOV4gO({bWo!Wx;@xE0pOFz z;LwiWlQ#^`S}>dqgScIq9Te5!6)1niOy6{(6;aB< z{t$U_f7hO81_w@iLhn0Iz^&8cp}H(~AV6B)AFUFwLr%!;rcA9rq)cv{X{K#DVl@@1 zs`Ps6JNVI#@c0$bTAu{ByT>tk!p)PO@WuY8K$&6$nWk3IjzAoVt6W==&g7+u2G3Y+ zRsUGoB`Qfm61ZCY$ARBK-@A`n1t8q+{snAr6+>uTqoAz?#8sg)4#cI@KJKB=7^270 zt1%+?(%Nt)+xyOw(9<~%dJaDXy+hQr6~_Zguf}9rgVclGkdBp? zqoRa5$X>Dxt{$5NUmu?cy`AIX+G!u?`)mpH9-j(5M<>D{3UlCje+XAPe${7wJM7v< zNqX;2`vhojz;N%|A2j5Mi1>Qw+~tD_?=g1yp2I(dfv@%f9etnYLC>LwSe%-GSyJd{Jo?fIy+p-l*W5p|EcY z;Zy(``Lx^)-V;7 zGhEpOdpFNxmnKrK3P8AqcZ%!BC&1F&9EeiY4Qb1X2T(Gq;EjpHc|cD2t_QYJl0uL7 z0A_E8yxdfr@OZ*C%EFb|Em%ack&tHIyqTO(E_tVginctq6s{#;6goY|LgemWYf>QwqYxOuoB}86>3bI)Nu# zv=*QhQBvmUSPjyAKhkc#?+{4(T9>!k1E%tdP_UdCBbnGuOm&6reBUuB=?z>tV9EiX=MS3%ZSgHmrO&7)+RgVJqvy!a;l?LvxFUQLzV7yeo=z9M1_9?LV<`A;VDgqj zt|^aDrq=9<)frD?qI(bB@yvIL@VJKV?W;vs|9eL0o_39-xHiC2X10}b;0c}de9smFU07*qoM6N<$f)GtIYybcN literal 0 HcmV?d00001 diff --git a/modules/servers/ArkHostHetznerVPS/template/img/vnc.png b/modules/servers/ArkHostHetznerVPS/template/img/vnc.png new file mode 100644 index 0000000000000000000000000000000000000000..8793f9539f1a5614524f60e1478fa0c108901de4 GIT binary patch literal 2970 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'; + } + }, 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; +} \ No newline at end of file diff --git a/modules/servers/ArkHostHetznerVPS/template/style.css b/modules/servers/ArkHostHetznerVPS/template/style.css new file mode 100644 index 0000000..3697d02 --- /dev/null +++ b/modules/servers/ArkHostHetznerVPS/template/style.css @@ -0,0 +1,604 @@ +/* ArkHost Hetzner VPS Module Styles */ + +.arkhost-vps-container { + font-family: 'Arial', sans-serif; + margin: 15px 0; +} +.arkhost-vps-container .title-block { + color: #575d6f; + margin-top: 20px; + font-size: 16px; + font-weight: 600; +} +.arkhost-vps-container .dashboard-value { + font-size: 18px; + color: #3c7fb4; +} +.arkhost-vps-container .usage-details { + font-size: 14px; + color: #888; + margin-top: 5px; +} +.arkhost-vps-container .progress { + height: 20px; + margin-bottom: 20px; +} +.arkhost-vps-container .nav-tabs { + border-bottom: 1px solid #dee2e6; +} +.arkhost-vps-container .nav-tabs .nav-link { + border: none; + color: #6c757d; + font-weight: 500; + border-radius: 12px 12px 0 0; + padding: 12px 20px; + transition: all 0.2s ease; + margin-right: 4px; +} +.arkhost-vps-container .nav-tabs .nav-link:hover { + background-color: #f8f9fa; + color: #495057; +} +.arkhost-vps-container .nav-tabs .nav-link.active { + background-color: #e9ecef; + color: #495057; + border-radius: 12px 12px 0 0; +} +.arkhost-vps-container .overview-label { + color: #575d6f; + font-weight: 600; + margin-bottom: 5px; + font-size: 14px; +} +.arkhost-vps-container .submit-btn { + background: linear-gradient(135deg, #3c7fb4 0%, #2d5a87 100%); + color: white; + border: none; + padding: 10px 20px; + border-radius: 12px; + font-weight: 600; + transition: all 0.3s ease; +} +.arkhost-vps-container .danger-btn { + background: linear-gradient(135deg, #dc3545 0%, #c82333 100%); + color: white; + border: none; + padding: 10px 20px; + border-radius: 12px; + font-weight: 600; + transition: all 0.3s ease; +} +.arkhost-vps-container .head { + border-bottom: 2px solid #3c7fb4; + margin-bottom: 20px; + padding-bottom: 15px; + margin-top: 10px; +} +.arkhost-vps-container .head img { + width: 32px; + height: 32px; + margin-right: 10px; + margin-top: -8px; + vertical-align: middle; +} +.arkhost-vps-container .v-tabs-container .nav-link { + padding: 10px 16px; + margin: 3px 0; + border-radius: 12px; + transition: all 0.2s ease; + color: #6c757d; + font-weight: 500; + text-decoration: none; + display: block; +} +.arkhost-vps-container .v-tabs-container .nav-link:hover { + background-color: #f8f9fa; + color: #495057; + text-decoration: none; +} +.arkhost-vps-container .v-tabs-container .nav-link.active { + background-color: #e9ecef; + color: #495057; +} +.arkhost-vps-container .os_badge { + margin: 8px; + border-radius: 16px; + transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); + min-height: 140px; + width: 100%; + max-width: 400px !important; + background: #ffffff; + border: none; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); + position: relative; + z-index: 1; +} + +.arkhost-vps-container .os_badge:hover { + transform: translateY(-6px); + box-shadow: 0 12px 40px rgba(60, 127, 180, 0.15); + z-index: 10; +} + +.arkhost-vps-container .os_badge .dropdown-toggle { + background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%); + border: 2px solid #e9ecef; + border-radius: 16px; + padding: 24px 20px; + transition: all 0.3s ease; + position: relative; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + justify-content: center; + height: 100%; + min-height: 140px; +} + + + +.arkhost-vps-container .os_badge .dropdown-toggle:hover { + border-color: #3c7fb4; + background: linear-gradient(135deg, #ffffff 0%, #f0f4f8 100%); +} + +.arkhost-vps-container .os_badge .media-body { + min-width: 0; + flex: 1; + z-index: 2; + position: relative; + margin-top: 6px; + text-align: center; +} + +.arkhost-vps-container .os_badge .distro_name { + font-size: 1.1rem; + line-height: 1.3; + margin-bottom: 4px; + word-wrap: break-word; + overflow-wrap: break-word; + font-weight: 600; + color: #2c3e50; +} + +.arkhost-vps-container .os_badge .version { + font-size: 0.85rem; + line-height: 1.2; + color: #6c757d; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.arkhost-vps-container .distro_img { + width: 48px; + height: 48px; + margin-right: 0; + margin-top: 8px; + margin-bottom: 6px; + border-radius: 50%; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: all 0.3s ease; + z-index: 2; + position: relative; +} + +.arkhost-vps-container .os_badge:hover .distro_img { + transform: scale(1.1) rotate(5deg); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2); +} + +/* Dropdown Menu Enhancements */ +.arkhost-vps-container .os_badge .dropdown-menu { + border-radius: 12px; + border: none; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15); + padding: 8px 0; + margin-top: 8px; + z-index: 1050; + min-width: 100%; +} + +.arkhost-vps-container .os_badge .dropdown-item { + padding: 12px 20px; + transition: all 0.2s ease; + color: #495057; + font-weight: 500; +} + +.arkhost-vps-container .os_badge .dropdown-item:hover { + background: linear-gradient(135deg, #3c7fb4 0%, #2d5a87 100%); + color: white; + transform: translateX(4px); +} +#loading { + text-align: center; + padding: 2rem; +} + +/* Rounded borders for containers */ +.arkhost-vps-container .border { + border-radius: 12px !important; + border: 1px solid #e3e6f0 !important; +} + +/* VPS Control Button Styles */ +.arkhost-vps-container .vps-control-btn { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 8px 16px; + border-radius: 20px; + margin: 8px 4px; + transition: all 0.2s ease; + text-decoration: none; + color: white; + font-size: 0.9rem; + font-weight: 500; + min-height: 36px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + border: none; + position: relative; + overflow: hidden; +} + +.arkhost-vps-container .vps-control-btn:hover { + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(0,0,0,0.15); + text-decoration: none; + color: white; +} + +.arkhost-vps-container .vps-control-btn:active { + transform: translateY(0); + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +.arkhost-vps-container .vps-control-btn.start-btn { + background: linear-gradient(45deg, #28a745, #34ce57); + color: white; +} + +.arkhost-vps-container .vps-control-btn.start-btn:hover { + background: linear-gradient(45deg, #218838, #28a745); + color: white; +} + +.arkhost-vps-container .vps-control-btn.stop-btn { + background: linear-gradient(45deg, #dc3545, #ff4757); + color: white; +} + +.arkhost-vps-container .vps-control-btn.stop-btn:hover { + background: linear-gradient(45deg, #c82333, #dc3545); + color: white; +} + +.arkhost-vps-container .vps-control-btn.restart-btn { + background: linear-gradient(45deg, #fd7e14, #ff9f43); + color: white; +} + +.arkhost-vps-container .vps-control-btn.restart-btn:hover { + background: linear-gradient(45deg, #e65100, #fd7e14); + color: white; +} + +.arkhost-vps-container .vps-control-btn.vnc-btn { + background: linear-gradient(45deg, #6f42c1, #8854d0); + color: white; +} + +.arkhost-vps-container .vps-control-btn.vnc-btn:hover { + background: linear-gradient(45deg, #5a32a3, #6f42c1); + color: white; +} + +.arkhost-vps-container .vps-control-btn.vnc-btn img { + width: 18px; + height: 18px; + margin-right: 6px; +} + +.arkhost-vps-container .vps-control-btn i { + margin-right: 6px; + font-size: 1rem; +} + +/* Mobile Responsive - Stack VPS Control Buttons Vertically */ +@media (max-width: 767px) { + .arkhost-vps-container .vps-control-btn { + display: block; + width: 100%; + margin: 8px 0; + text-align: center; + } +} + +/* Graph Time Selection Buttons */ +.arkhost-vps-container .graph-time-btn { + background: #f8f9fa; + color: #6c757d; + border: 1px solid #dee2e6; + padding: 8px 14px; + border-radius: 12px; + margin: 0 3px; + font-weight: 500; + transition: all 0.2s ease; + text-decoration: none; + display: inline-block; + cursor: pointer; + font-size: 0.9rem; +} + +.arkhost-vps-container .graph-time-btn:hover { + background: #e9ecef; + text-decoration: none; +} + +.arkhost-vps-container .graph-time-btn.active { + background: #e9ecef; + color: #495057; + border-color: #ced4da; +} + +.arkhost-vps-container .graph-time-btn.active:hover { + background: #dee2e6; +} + +/* OS Selection Enhancements */ +.arkhost-vps-container .os_badge { + margin-bottom: 20px; +} + +.arkhost-vps-container .os_badge .dropdown-toggle::after { + display: inline-block; + margin-left: .255em; + vertical-align: .255em; + content: ""; + border-top: .3em solid; + border-right: .3em solid transparent; + border-bottom: 0; + border-left: .3em solid transparent; + margin-bottom: 10px; +} + +.arkhost-vps-container .os_badge .dropdown-toggle:hover::after { + border-top-color: #495057; +} + +.arkhost-vps-container .os_badge .dropdown-toggle.btn-success { + border: 2px solid #28a745 !important; + background: linear-gradient(135deg, #f8fff9 0%, #e8f5e8 100%); + box-shadow: 0 6px 25px rgba(40, 167, 69, 0.3); + color: #155724 !important; + position: relative; +} + +.arkhost-vps-container .os_badge .dropdown-toggle.btn-success::before { + content: '✓'; + position: absolute; + top: 8px; + right: 12px; + font-size: 18px; + color: #28a745; + font-weight: bold; +} + +.arkhost-vps-container .os_badge .dropdown-toggle.btn-success .distro_name { + color: #155724 !important; + font-weight: 600; +} + +.arkhost-vps-container .os_badge .dropdown-toggle.btn-success .version { + color: #155724 !important; +} + +.arkhost-vps-container .os_badge .dropdown-item.active { + background-color: #007bff !important; + color: white !important; + font-weight: bold; + position: relative; +} + +.arkhost-vps-container .os_badge .dropdown-item.active::after { + content: "✓"; + position: absolute; + right: 15px; + font-weight: bold; + color: white; +} + +.arkhost-vps-container .os_badge .dropdown-item:hover { + background-color: #007bff; + color: white !important; +} + +.arkhost-vps-container .os_badge .dropdown-item.active:hover { + background-color: #0056b3 !important; + color: white !important; +} + +/* Fix dropdown menu positioning and styling */ +.arkhost-vps-container .os_badge .dropdown-menu { + z-index: 1050; + border: 1px solid #dee2e6; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0,0,0,0.15); + padding: 8px 0; + margin-top: 4px; + max-height: 300px; + overflow-y: auto; + overflow-x: hidden; +} + +.arkhost-vps-container .os_badge .dropdown-item { + padding: 8px 16px; + font-size: 0.9rem; + transition: all 0.2s ease; + border: none; + background: none; +} + +.arkhost-vps-container .os_badge .version .fa-check { + color: #28a745 !important; + margin-left: 5px; + animation: checkmark-appear 0.3s ease-in; +} + +@keyframes checkmark-appear { + 0% { opacity: 0; transform: scale(0.5); } + 100% { opacity: 1; transform: scale(1); } +} + +/* Custom Notification System */ +.arkhost-notification { + position: fixed; + top: 20px; + right: 20px; + min-width: 300px; + max-width: 400px; + z-index: 9999; + padding: 16px 20px; + border-radius: 8px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); + color: white; + font-weight: 500; + transform: translateX(450px); + transition: all 0.3s ease; + display: flex; + align-items: center; + gap: 12px; + font-family: 'Arial', sans-serif; +} + +.arkhost-notification.show { + transform: translateX(0); +} + +.arkhost-notification.success { + background: linear-gradient(135deg, #28a745 0%, #20c997 100%); +} + +.arkhost-notification.error { + background: linear-gradient(135deg, #dc3545 0%, #fd7e14 100%); +} + +.arkhost-notification.warning { + background: linear-gradient(135deg, #ffc107 0%, #fd7e14 100%); + color: #212529; +} + +.arkhost-notification.info { + background: linear-gradient(135deg, #17a2b8 0%, #6f42c1 100%); +} + +.arkhost-notification i { + font-size: 20px; + flex-shrink: 0; +} + +.arkhost-notification .close-btn { + margin-left: auto; + background: none; + border: none; + color: inherit; + font-size: 18px; + cursor: pointer; + padding: 0; + opacity: 0.8; + transition: opacity 0.2s ease; +} + +.arkhost-notification .close-btn:hover { + opacity: 1; +} + +/* Custom Confirm Dialog */ +.arkhost-confirm-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 10000; + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; +} + +.arkhost-confirm-overlay.show { + opacity: 1; + visibility: visible; +} + +.arkhost-confirm-dialog { + background: white; + border-radius: 12px; + padding: 24px; + max-width: 400px; + width: 90%; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); + transform: scale(0.8); + transition: transform 0.3s ease; + font-family: 'Arial', sans-serif; +} + +.arkhost-confirm-overlay.show .arkhost-confirm-dialog { + transform: scale(1); +} + +.arkhost-confirm-title { + font-size: 18px; + font-weight: 600; + color: #2c3e50; + margin-bottom: 12px; + display: flex; + align-items: center; + gap: 8px; +} + +.arkhost-confirm-message { + color: #6c757d; + margin-bottom: 20px; + line-height: 1.5; +} + +.arkhost-confirm-buttons { + display: flex; + gap: 12px; + justify-content: flex-end; +} + +.arkhost-confirm-btn { + padding: 8px 16px; + border: none; + border-radius: 6px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; + font-family: inherit; +} + +.arkhost-confirm-btn.cancel { + background: #6c757d; + color: white; +} + +.arkhost-confirm-btn.cancel:hover { + background: #5a6268; +} + +.arkhost-confirm-btn.confirm { + background: #dc3545; + color: white; +} + +.arkhost-confirm-btn.confirm:hover { + background: #c82333; +} \ No newline at end of file