+
+';
+ 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
+ *}
+
+
+
+
+
+
+
+
';
+
+ // 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 =
+ '
';
+ }
+}
+
+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 += '';
+ 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 += '