mirror of
https://gitlab.com/ArkHost/WHMCS-ArkHost-HetznerVPS.git
synced 2026-07-24 15:55:53 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a19e78d51 | ||
|
|
fc2d2d4df6 |
@@ -59,7 +59,7 @@ WHMCS server module for Hetzner Cloud VPS management.
|
|||||||
|
|
||||||
- Server Type: `cx23`, `cx33`, `cx43`, `cx53` (Cost-Optimized), `cpx11`, `cpx21`, `cax11`, `ccx13`, etc.
|
- Server Type: `cx23`, `cx33`, `cx43`, `cx53` (Cost-Optimized), `cpx11`, `cpx21`, `cax11`, `ccx13`, etc.
|
||||||
- Operating System: `ubuntu-22.04`, `debian-11`, `rocky-9`, etc.
|
- Operating System: `ubuntu-22.04`, `debian-11`, `rocky-9`, etc.
|
||||||
- Datacenter: `fsn1`, `nbg1`, `hel1`, `ash`, `hil`, `sin`
|
- Location: `fsn1`, `nbg1`, `hel1`, `ash`, `hil`, `sin`
|
||||||
- Backups: On/Off
|
- Backups: On/Off
|
||||||
- Create Floating IP: On/Off
|
- Create Floating IP: On/Off
|
||||||
- Cloud-Init YAML: Optional custom cloud-init configuration
|
- Cloud-Init YAML: Optional custom cloud-init configuration
|
||||||
@@ -84,17 +84,19 @@ Create in Setup → Products/Services → Configurable Options:
|
|||||||
- `No|None`
|
- `No|None`
|
||||||
- `Yes|1 Floating IP`
|
- `Yes|1 Floating IP`
|
||||||
|
|
||||||
**2\. Datacenter Selection**
|
**2\. Location Selection**
|
||||||
|
|
||||||
- Option Name: `datacenter`
|
- Option Name: `Location`
|
||||||
- Option Type: `Dropdown`
|
- Option Type: `Dropdown`
|
||||||
- Options:
|
- Options:
|
||||||
- `fsn1-dc14|Falkenstein, Germany`
|
- `fsn1|Falkenstein, Germany`
|
||||||
- `nbg1-dc3|Nuremberg, Germany`
|
- `nbg1|Nuremberg, Germany`
|
||||||
- `hel1-dc2|Helsinki, Finland`
|
- `hel1|Helsinki, Finland`
|
||||||
- `ash-dc1|Ashburn, USA`
|
- `ash|Ashburn, USA`
|
||||||
- `hil-dc1|Hillsboro, USA`
|
- `hil|Hillsboro, USA`
|
||||||
- `sin-dc1|Singapore, Singapore`
|
- `sin|Singapore, Singapore`
|
||||||
|
|
||||||
|
Existing configurable options named `datacenter` remain supported. Legacy values such as `fsn1-dc14` are automatically converted to their corresponding location (`fsn1`) when provisioning.
|
||||||
|
|
||||||
**3\. Server Type**
|
**3\. Server Type**
|
||||||
|
|
||||||
@@ -306,33 +308,22 @@ runcmd:
|
|||||||
|
|
||||||
Enable module debugging: Configuration → System Logs → Module Log
|
Enable module debugging: Configuration → System Logs → Module Log
|
||||||
|
|
||||||
## Support Policy
|
## Support
|
||||||
|
|
||||||
This is free, open-source software provided AS-IS.
|
This module is free and open source, and we maintain it in our spare time. We hope it makes running Hetzner Cloud on WHMCS a little easier for you.
|
||||||
|
|
||||||
**NO SUPPORT** is provided except through paid channels:
|
If something isn't working as expected:
|
||||||
- Premium Support: €100/hour (2 hour minimum)
|
|
||||||
- Email: support@arkhost.com (paid support only)
|
|
||||||
|
|
||||||
### Before Requesting Support
|
1. Check the module log first (Configuration → System Logs → Module Log). It usually points straight to the cause.
|
||||||
- English only
|
2. Open an issue with what you saw, the steps that led to it, and the relevant log output. The more detail you share, the quicker we can help.
|
||||||
- Must provide complete error logs
|
|
||||||
- Must provide steps to reproduce
|
|
||||||
- Vague reports like "doesn't work" will be ignored
|
|
||||||
|
|
||||||
### GitHub Issues
|
We can't promise a response time, but we read every report and genuinely appreciate bug reports, ideas, and pull requests. Contributions are always welcome.
|
||||||
- For documented bugs only
|
|
||||||
- Requires complete technical information
|
|
||||||
- Issues lacking proper information will be closed immediately
|
|
||||||
- This is not a support forum
|
|
||||||
|
|
||||||
If you need help with basic installation or configuration, purchase premium support.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT License - see [LICENSE](LICENSE) file for details.
|
MIT License - see [LICENSE](LICENSE) file for details.
|
||||||
|
|
||||||
Free and open source. Commercial support available.
|
Free and open source. Use it, fork it, improve it.
|
||||||
|
|
||||||
## Other ArkHost Modules
|
## Other ArkHost Modules
|
||||||
|
|
||||||
@@ -340,6 +331,13 @@ Check out our other WHMCS modules at [arkhost.com/whmcs-modules.php](https://ar
|
|||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### v1.2.5
|
||||||
|
**Fixed:**
|
||||||
|
- Restored server provisioning after Hetzner removed the deprecated `datacenter` request property on July 1, 2026
|
||||||
|
- Replaced datacenter discovery with the Hetzner Locations API
|
||||||
|
- Updated client-area location display for the current server response format
|
||||||
|
- Added automatic conversion of legacy datacenter values such as `fsn1-dc14` to `fsn1`
|
||||||
|
|
||||||
### v1.2.4
|
### v1.2.4
|
||||||
**Fixed:**
|
**Fixed:**
|
||||||
- IP address now stored on the service at provisioning (and backfilled for existing services) so it displays in WHMCS instead of showing blank
|
- IP address now stored on the service at provisioning (and backfilled for existing services) so it displays in WHMCS instead of showing blank
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* WHMCS Server Module - Hetzner VPS
|
* WHMCS Server Module - Hetzner VPS
|
||||||
*
|
*
|
||||||
* @package WHMCS
|
* @package WHMCS
|
||||||
* @version 1.2.4
|
* @version 1.2.5
|
||||||
* @copyright Copyright (c) ArkHost 2025
|
* @copyright Copyright (c) ArkHost 2025
|
||||||
* @author ArkHost <support@arkhost.com>
|
* @author ArkHost <support@arkhost.com>
|
||||||
* @link https://arkhost.com
|
* @link https://arkhost.com
|
||||||
@@ -104,6 +104,57 @@ function ArkHostHetznerVPS_GetConfigurableOption(array $params, $optionName) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a legacy Hetzner datacenter name (for example fsn1-dc14) to the
|
||||||
|
* corresponding location name (fsn1). Location names are normalized to
|
||||||
|
* lowercase, while numeric IDs remain unchanged.
|
||||||
|
*/
|
||||||
|
function ArkHostHetznerVPS_NormalizeLocation($location) {
|
||||||
|
if (!is_scalar($location)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$location = trim((string)$location);
|
||||||
|
if ($location === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/^([a-z0-9]+)-dc[0-9]+$/i', $location, $matches)) {
|
||||||
|
return strtolower($matches[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return strtolower($location);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the server location while remaining compatible with configurable
|
||||||
|
* options named either "Location" or the former "Datacenter".
|
||||||
|
*/
|
||||||
|
function ArkHostHetznerVPS_GetLocationOption(array $params, $default = NULL) {
|
||||||
|
foreach (array('configoptions', 'customfields') as $collection) {
|
||||||
|
if (!isset($params[$collection]) || !is_array($params[$collection])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer the current option name if both current and legacy options
|
||||||
|
// happen to exist on the same product.
|
||||||
|
foreach (array('location', 'datacenter') as $optionName) {
|
||||||
|
foreach ($params[$collection] as $name => $value) {
|
||||||
|
$normalizedName = strtolower(trim((string)$name));
|
||||||
|
if ($normalizedName === $optionName && $value !== '') {
|
||||||
|
return ArkHostHetznerVPS_NormalizeLocation($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep using the original module setting key so existing WHMCS products
|
||||||
|
// retain their configoption3 value after upgrading.
|
||||||
|
return ArkHostHetznerVPS_NormalizeLocation(
|
||||||
|
ArkHostHetznerVPS_GetOption($params, 'datacenter', $default)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function ArkHostHetznerVPS_API(array $params) {
|
function ArkHostHetznerVPS_API(array $params) {
|
||||||
$url = 'https://api.hetzner.cloud/v1/';
|
$url = 'https://api.hetzner.cloud/v1/';
|
||||||
$data = [];
|
$data = [];
|
||||||
@@ -125,8 +176,8 @@ function ArkHostHetznerVPS_API(array $params) {
|
|||||||
$method = 'GET';
|
$method = 'GET';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Datacenters':
|
case 'Locations':
|
||||||
$url .= 'datacenters';
|
$url .= 'locations?per_page=50';
|
||||||
$method = 'GET';
|
$method = 'GET';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -160,10 +211,11 @@ function ArkHostHetznerVPS_API(array $params) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add datacenter if specified
|
// Hetzner removed the datacenter request property on 1 July 2026.
|
||||||
$datacenter = ArkHostHetznerVPS_GetOption($params, 'datacenter');
|
// Normalize legacy values such as fsn1-dc14 before sending location.
|
||||||
if ($datacenter) {
|
$location = ArkHostHetznerVPS_GetLocationOption($params);
|
||||||
$data['datacenter'] = $datacenter;
|
if ($location) {
|
||||||
|
$data['location'] = $location;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle backups
|
// Handle backups
|
||||||
@@ -975,9 +1027,11 @@ function ArkHostHetznerVPS_ConfigOptions() {
|
|||||||
'Type' => 'dropdown',
|
'Type' => 'dropdown',
|
||||||
'Options' => array(),
|
'Options' => array(),
|
||||||
),
|
),
|
||||||
|
// Keep the original array key and position for existing WHMCS product
|
||||||
|
// settings. Only the Hetzner-facing value and admin label have changed.
|
||||||
'datacenter' => array(
|
'datacenter' => array(
|
||||||
'FriendlyName' => 'Datacenter',
|
'FriendlyName' => 'Location',
|
||||||
'Description' => 'The datacenter location for the server.',
|
'Description' => 'The Hetzner location for the server (Configurable option: Location or legacy datacenter).',
|
||||||
'Type' => 'dropdown',
|
'Type' => 'dropdown',
|
||||||
'Options' => array(),
|
'Options' => array(),
|
||||||
),
|
),
|
||||||
@@ -993,7 +1047,7 @@ function ArkHostHetznerVPS_ConfigOptions() {
|
|||||||
),
|
),
|
||||||
'floating_ip_location' => array(
|
'floating_ip_location' => array(
|
||||||
'FriendlyName' => 'Floating IP Location',
|
'FriendlyName' => 'Floating IP Location',
|
||||||
'Description' => 'Location for floating IPs (same as datacenter)',
|
'Description' => 'Location for floating IPs (normally the same as the server location).',
|
||||||
'Type' => 'dropdown',
|
'Type' => 'dropdown',
|
||||||
'Options' => array(),
|
'Options' => array(),
|
||||||
),
|
),
|
||||||
@@ -1082,19 +1136,31 @@ function ArkHostHetznerVPS_ConfigOptions() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch datacenters
|
// Fetch locations. Hetzner's datacenter endpoints are deprecated
|
||||||
$params['action'] = 'Datacenters';
|
// and are scheduled for removal after 1 October 2026.
|
||||||
$datacenters = ArkHostHetznerVPS_API($params);
|
$params['action'] = 'Locations';
|
||||||
|
$locations = ArkHostHetznerVPS_API($params);
|
||||||
|
|
||||||
if (isset($datacenters['datacenters'])) {
|
if (isset($locations['locations'])) {
|
||||||
foreach ($datacenters['datacenters'] as $datacenter) {
|
foreach ($locations['locations'] as $location) {
|
||||||
$array['datacenter']['Options'] += array(
|
if (empty($location['name'])) {
|
||||||
$datacenter['name'] => $datacenter['description'] . ' (' . $datacenter['location']['city'] . ')'
|
continue;
|
||||||
);
|
}
|
||||||
// Also populate floating IP locations
|
|
||||||
$array['floating_ip_location']['Options'] += array(
|
$place = array_filter(array(
|
||||||
$datacenter['location']['name'] => $datacenter['location']['city'] . ', ' . $datacenter['location']['country']
|
$location['city'] ?? '',
|
||||||
);
|
$location['country'] ?? ''
|
||||||
|
));
|
||||||
|
$description = !empty($location['description'])
|
||||||
|
? $location['description']
|
||||||
|
: $location['name'];
|
||||||
|
$label = $description;
|
||||||
|
if (!empty($place)) {
|
||||||
|
$label .= ' (' . implode(', ', $place) . ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
$array['datacenter']['Options'][$location['name']] = $label;
|
||||||
|
$array['floating_ip_location']['Options'][$location['name']] = $label;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1218,7 +1284,9 @@ function ArkHostHetznerVPS_CreateAccount(array $params) {
|
|||||||
$floatingIpParams = $params;
|
$floatingIpParams = $params;
|
||||||
$floatingIpParams['action'] = 'Create Floating IP';
|
$floatingIpParams['action'] = 'Create Floating IP';
|
||||||
$floatingIpParams['ip_type'] = $ipType;
|
$floatingIpParams['ip_type'] = $ipType;
|
||||||
$floatingIpParams['location'] = ArkHostHetznerVPS_GetOption($params, 'floating_ip_location');
|
$floatingIpParams['location'] = ArkHostHetznerVPS_NormalizeLocation(
|
||||||
|
ArkHostHetznerVPS_GetOption($params, 'floating_ip_location')
|
||||||
|
);
|
||||||
$floatingIpParams['description'] = 'Server ID: ' . $create['server']['id'];
|
$floatingIpParams['description'] = 'Server ID: ' . $create['server']['id'];
|
||||||
$floatingIpParams['assign_to_server'] = true;
|
$floatingIpParams['assign_to_server'] = true;
|
||||||
|
|
||||||
@@ -1691,8 +1759,9 @@ function ArkHostHetznerVPS_ClientAreaAPI(array $params) {
|
|||||||
$createParams = $params;
|
$createParams = $params;
|
||||||
$createParams['action'] = 'Create Floating IP';
|
$createParams['action'] = 'Create Floating IP';
|
||||||
$createParams['ip_type'] = 'ipv4';
|
$createParams['ip_type'] = 'ipv4';
|
||||||
$createParams['location'] = ArkHostHetznerVPS_GetOption($params, 'floating_ip_location') ?:
|
$createParams['location'] = ArkHostHetznerVPS_NormalizeLocation(
|
||||||
ArkHostHetznerVPS_GetOption($params, 'datacenter');
|
ArkHostHetznerVPS_GetOption($params, 'floating_ip_location')
|
||||||
|
) ?: ArkHostHetznerVPS_GetLocationOption($params);
|
||||||
$createParams['description'] = 'WHMCS Service ID: ' . $params['serviceid'];
|
$createParams['description'] = 'WHMCS Service ID: ' . $params['serviceid'];
|
||||||
$createParams['assign_to_server'] = true;
|
$createParams['assign_to_server'] = true;
|
||||||
|
|
||||||
@@ -2485,21 +2554,31 @@ function ArkHostHetznerVPS_ClientArea(array $params) {
|
|||||||
$serverInfo['bandwidth'] = 20480; // 20TB in GB (20 * 1024)
|
$serverInfo['bandwidth'] = 20480; // 20TB in GB (20 * 1024)
|
||||||
$serverInfo['bandwidth_used'] = 0; // Not available via API
|
$serverInfo['bandwidth_used'] = 0; // Not available via API
|
||||||
|
|
||||||
// Add datacenter info - format as "City, Country"
|
// Hetzner now returns location directly on the server. Retain a fallback
|
||||||
$city = isset($serverInfo['datacenter']['location']['city']) ? $serverInfo['datacenter']['location']['city'] : '';
|
// for cached/older responses that still contain datacenter.location.
|
||||||
$country = isset($serverInfo['datacenter']['location']['country']) ? $serverInfo['datacenter']['location']['country'] : '';
|
$locationInfo = array();
|
||||||
|
if (isset($serverInfo['location']) && is_array($serverInfo['location'])) {
|
||||||
if ($city && $country) {
|
$locationInfo = $serverInfo['location'];
|
||||||
$serverInfo['datacenter'] = $city . ', ' . $country;
|
} elseif (isset($serverInfo['datacenter']['location']) && is_array($serverInfo['datacenter']['location'])) {
|
||||||
} elseif ($city) {
|
$locationInfo = $serverInfo['datacenter']['location'];
|
||||||
$serverInfo['datacenter'] = $city;
|
|
||||||
} elseif ($country) {
|
|
||||||
$serverInfo['datacenter'] = $country;
|
|
||||||
} else {
|
|
||||||
$serverInfo['datacenter'] = 'N/A';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$serverInfo['location'] = $city ?: 'N/A';
|
$city = $locationInfo['city'] ?? '';
|
||||||
|
$country = $locationInfo['country'] ?? '';
|
||||||
|
|
||||||
|
if ($city && $country) {
|
||||||
|
$locationDisplay = $city . ', ' . $country;
|
||||||
|
} elseif ($city) {
|
||||||
|
$locationDisplay = $city;
|
||||||
|
} elseif ($country) {
|
||||||
|
$locationDisplay = $country;
|
||||||
|
} else {
|
||||||
|
$locationDisplay = 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
$serverInfo['location_display'] = $locationDisplay;
|
||||||
|
// Preserve the former display key for third-party/custom templates.
|
||||||
|
$serverInfo['datacenter'] = $locationDisplay;
|
||||||
|
|
||||||
// Get root password with expiration check (72 hours)
|
// Get root password with expiration check (72 hours)
|
||||||
$passwordSetTime = $params['model']->serviceProperties->get('ArkHostHetznerVPS|Password Set Time');
|
$passwordSetTime = $params['model']->serviceProperties->get('ArkHostHetznerVPS|Password Set Time');
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* WHMCS Server Module - Hetzner VPS Hooks
|
* WHMCS Server Module - Hetzner VPS Hooks
|
||||||
*
|
*
|
||||||
* @package WHMCS
|
* @package WHMCS
|
||||||
* @version 1.2.4
|
* @version 1.2.5
|
||||||
* @copyright Copyright (c) ArkHost 2025
|
* @copyright Copyright (c) ArkHost 2025
|
||||||
* @author ArkHost <support@arkhost.com>
|
* @author ArkHost <support@arkhost.com>
|
||||||
* @link https://arkhost.com
|
* @link https://arkhost.com
|
||||||
@@ -70,4 +70,3 @@ add_hook('ClientAreaHeadOutput', 1, function($vars) {
|
|||||||
</style>
|
</style>
|
||||||
HTML;
|
HTML;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -207,7 +207,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-muted">{$ADDONLANG.Overview.Location}:</td>
|
<td class="text-muted">{$ADDONLANG.Overview.Location}:</td>
|
||||||
<td><i class="fa fa-map-marker-alt mr-1"></i>{$serverInfo['datacenter']}</td>
|
<td><i class="fa fa-map-marker-alt mr-1"></i>{$serverInfo['location_display']}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-muted">{$ADDONLANG.Overview.ServerType}:</td>
|
<td class="text-muted">{$ADDONLANG.Overview.ServerType}:</td>
|
||||||
|
|||||||
Reference in New Issue
Block a user