mirror of
https://gitlab.com/ArkHost/WHMCS-ArkHost-HetznerVPS.git
synced 2026-07-24 07:45:53 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
378b3a0ba3 |
@@ -39,14 +39,15 @@ WHMCS server module for Hetzner Cloud VPS management.
|
|||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
1. Upload files to `/path/to/whmcs/modules/servers/ArkHostHetznerVPS/`
|
1. Upload files to `/path/to/whmcs/modules/servers/ArkHostHetznerVPS/`
|
||||||
2. Create server and add it to a server group:
|
2. Upload `hooks.php` to `/path/to/whmcs/includes/hooks/` (for enhanced UI)
|
||||||
|
3. Create server and add it to a server group:
|
||||||
- Setup → Products/Services → Servers → Create New Group
|
- Setup → Products/Services → Servers → Create New Group
|
||||||
- Name: `Hetzner Cloud`
|
- Name: `Hetzner Cloud`
|
||||||
- Type: `ArkHostHetznerVPS`
|
- Type: `ArkHostHetznerVPS`
|
||||||
- Hostname: `localhost`
|
- Hostname: `localhost`
|
||||||
- Username: Hetzner Project ID (optional)
|
- Username: Hetzner Project ID (optional)
|
||||||
- Password: Hetzner Cloud API Token
|
- Password: Hetzner Cloud API Token
|
||||||
3. Create products:
|
4. Create products:
|
||||||
- Setup → Products/Services → Products/Services → Create New Product
|
- Setup → Products/Services → Products/Services → Create New Product
|
||||||
- Type: `VPS/Dedicated Server`
|
- Type: `VPS/Dedicated Server`
|
||||||
- Module: `ArkHostHetznerVPS`
|
- Module: `ArkHostHetznerVPS`
|
||||||
@@ -337,4 +338,60 @@ Free and open source. Commercial support available.
|
|||||||
|
|
||||||
Check out our other WHMCS modules at [arkhost.com/whmcs-modules.php](https://arkhost.com/whmcs-modules.php "https://arkhost.com/whmcs-modules.php")
|
Check out our other WHMCS modules at [arkhost.com/whmcs-modules.php](https://arkhost.com/whmcs-modules.php "https://arkhost.com/whmcs-modules.php")
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
### v1.2.1
|
||||||
|
**Added:**
|
||||||
|
- Shutdown button functionality (graceful server shutdown)
|
||||||
|
- Enhanced backup permission detection (automatically detects Hetzner-enabled backups)
|
||||||
|
- Improved Actions sidebar with custom Font Awesome icons
|
||||||
|
- VNC console opens in new tab
|
||||||
|
- hooks.php for cleaner UI (hides default WHMCS elements)
|
||||||
|
|
||||||
|
**Fixed:**
|
||||||
|
- Backup creation/deletion API response format
|
||||||
|
- Shutdown action now properly whitelisted in ClientAreaAPI
|
||||||
|
|
||||||
|
### v1.2.0
|
||||||
|
**Added:**
|
||||||
|
- Cloud-init support (optional per-product YAML configuration)
|
||||||
|
- CX Gen3 server types: CX23, CX33, CX43, CX53
|
||||||
|
- OS images: Debian 13, AlmaLinux 10, Rocky 10, CentOS Stream 10, openSUSE 15, Fedora 41/42
|
||||||
|
- Password expiration (72-hour visibility window)
|
||||||
|
|
||||||
|
**Fixed:**
|
||||||
|
- API pagination - server types and OS images now show all available options (per_page=50)
|
||||||
|
- Password reset timestamp tracking
|
||||||
|
- Password display in client area after reset
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- Cloud-init configuration examples
|
||||||
|
- Updated server types and OS lists
|
||||||
|
|
||||||
|
### v1.1.1
|
||||||
|
**Fixed:**
|
||||||
|
- Firewall rules now accept source 0.0.0.0/0 for all IPs
|
||||||
|
- "ANY" protocol creates both TCP and UDP rules automatically
|
||||||
|
- Outbound firewall rules now use correct destination_ips field
|
||||||
|
|
||||||
|
**Added:**
|
||||||
|
- Incoming/Outgoing direction support for firewall rules
|
||||||
|
- Proper CIDR notation validation (supports /0, /4, /8, /16, etc.)
|
||||||
|
|
||||||
|
**Improved:**
|
||||||
|
- Removed description/note field from firewall interface
|
||||||
|
- Changed "Source/Destination" column to "IP/CIDR" for clarity
|
||||||
|
- Translated all firewall column headers to all supported languages
|
||||||
|
- Cleaned up unused language strings
|
||||||
|
|
||||||
|
### v1.1
|
||||||
|
**Fixed:**
|
||||||
|
- Firewall Display: Removed fake "default open" rules that didn't exist in Hetzner dashboard
|
||||||
|
- Firewall Creation: Fixed "name already used" error with unique timestamp naming
|
||||||
|
- Firewall Association: Fixed API errors when attaching firewalls to servers
|
||||||
|
- API Compatibility: Corrected data formats for Hetzner API (ports as strings, proper server object structure)
|
||||||
|
|
||||||
|
### v1.0
|
||||||
|
- Initial release
|
||||||
|
|
||||||
© 2025 ArkHost
|
© 2025 ArkHost
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* WHMCS Server Module - Hetzner VPS
|
* WHMCS Server Module - Hetzner VPS
|
||||||
*
|
*
|
||||||
* @package WHMCS
|
* @package WHMCS
|
||||||
* @version 1.1.1
|
* @version 1.2.1
|
||||||
* @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
|
||||||
@@ -1529,7 +1529,7 @@ function ArkHostHetznerVPS_ClientAreaAPI(array $params) {
|
|||||||
try {
|
try {
|
||||||
$action = App::getFromRequest('api');
|
$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');
|
$actions = array('Server Info', 'Graphs', 'Reinstall', 'Reboot', 'Stop', 'Shutdown', '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');
|
$results = array('result' => 'success');
|
||||||
|
|
||||||
if (in_array($action, $actions)) {
|
if (in_array($action, $actions)) {
|
||||||
@@ -1553,11 +1553,28 @@ function ArkHostHetznerVPS_ClientAreaAPI(array $params) {
|
|||||||
$backupsEnabled = true;
|
$backupsEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If not enabled in WHMCS settings, check if backups are actually enabled on Hetzner's side
|
||||||
if (!$backupsEnabled) {
|
if (!$backupsEnabled) {
|
||||||
return array(
|
try {
|
||||||
|
$serverInfoParams = $params;
|
||||||
|
$serverInfoParams['action'] = 'Server Info';
|
||||||
|
$serverInfoResult = ArkHostHetznerVPS_API($serverInfoParams);
|
||||||
|
|
||||||
|
// Check if the server has backups enabled (indicated by backup_window being set)
|
||||||
|
if (isset($serverInfoResult['server']['backup_window']) && $serverInfoResult['server']['backup_window'] !== null) {
|
||||||
|
$backupsEnabled = true;
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// If we can't check, assume not enabled
|
||||||
|
$backupsEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$backupsEnabled) {
|
||||||
|
return array('jsonResponse' => array(
|
||||||
'result' => 'error',
|
'result' => 'error',
|
||||||
'message' => 'Backups are not enabled for this service. Please upgrade your plan to enable backups.'
|
'message' => 'Backups are not enabled for this service. Please upgrade your plan to enable backups.'
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2046,6 +2063,7 @@ function ArkHostHetznerVPS_ClientAreaCustomButtonArray() {
|
|||||||
$_LANG['Start'] => 'Start',
|
$_LANG['Start'] => 'Start',
|
||||||
$_LANG['Stop'] => 'Stop',
|
$_LANG['Stop'] => 'Stop',
|
||||||
$_LANG['Restart'] => 'Reboot',
|
$_LANG['Restart'] => 'Reboot',
|
||||||
|
$_LANG['Shutdown'] => 'Shutdown',
|
||||||
$_LANG['VNC'] => 'VNC',
|
$_LANG['VNC'] => 'VNC',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WHMCS Server Module - Hetzner VPS Hooks
|
||||||
|
*
|
||||||
|
* @package WHMCS
|
||||||
|
* @version 1.2.1
|
||||||
|
* @copyright Copyright (c) ArkHost 2025
|
||||||
|
* @author ArkHost <support@arkhost.com>
|
||||||
|
* @link https://arkhost.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
use WHMCS\Database\Capsule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide default WHMCS product details and cog icons for ArkHostHetznerVPS products
|
||||||
|
* Injects CSS early to prevent flash of content
|
||||||
|
*/
|
||||||
|
add_hook('ClientAreaHeadOutput', 1, function($vars) {
|
||||||
|
// Only run on product details page
|
||||||
|
if (!isset($_GET['action']) || $_GET['action'] !== 'productdetails') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the service ID from the request
|
||||||
|
$serviceId = isset($_GET['id']) ? (int) $_GET['id'] : 0;
|
||||||
|
if (!$serviceId) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this service uses the ArkHostHetznerVPS module
|
||||||
|
try {
|
||||||
|
$service = Capsule::table('tblhosting')
|
||||||
|
->where('id', $serviceId)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (!$service || $service->servertype !== 'ArkHostHetznerVPS') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject CSS early in the HEAD to prevent flash
|
||||||
|
// This works for both active and suspended services
|
||||||
|
return <<<HTML
|
||||||
|
<style>
|
||||||
|
/* Hide WHMCS default product details (Domain, Username, Server Name, IP, Visit Website) for ArkHostHetznerVPS */
|
||||||
|
#domain > .row:nth-child(2),
|
||||||
|
#domain > .row:nth-child(3),
|
||||||
|
#domain > .row:nth-child(4) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
#domain > br {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
#domain > p {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide cog icons from custom action buttons IMMEDIATELY */
|
||||||
|
.list-group-item[href*="modop=custom"] i.fa-cog,
|
||||||
|
.list-group-item[href*="modop=custom"] i.fas.fa-cog,
|
||||||
|
.list-group-item[href*="modop=custom"] .fa-cog,
|
||||||
|
.list-group-item[href*="modop=custom"] i.fa-wrench,
|
||||||
|
a[href*="modop=custom"] i.fa-cog,
|
||||||
|
a[href*="modop=custom"] i.fas.fa-cog,
|
||||||
|
a[href*="modop=custom"] .fa-cog {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
HTML;
|
||||||
|
});
|
||||||
|
|
||||||
@@ -756,4 +756,66 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Wait for WHMCS jQuery to be ready and use it
|
||||||
|
jQuery(document).ready(function($) {
|
||||||
|
// Inject custom icons into sidebar action buttons
|
||||||
|
$('.list-group-item').each(function() {
|
||||||
|
var $link = $(this);
|
||||||
|
var href = $link.attr('href');
|
||||||
|
|
||||||
|
if (!href) return;
|
||||||
|
|
||||||
|
// Add custom Font Awesome icons based on action
|
||||||
|
if (href.indexOf('modop=custom&a=Start') !== -1) {
|
||||||
|
// Prepend play icon to Start button
|
||||||
|
$link.html('<i class="fa fa-play"></i> ' + $link.text().trim());
|
||||||
|
|
||||||
|
// Intercept click to use our API
|
||||||
|
$link.off('click').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
ArkHostHetznerVPS_API('Start', true);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
} else if (href.indexOf('modop=custom&a=Stop') !== -1) {
|
||||||
|
// Prepend stop icon to Stop button
|
||||||
|
$link.html('<i class="fa fa-stop"></i> ' + $link.text().trim());
|
||||||
|
|
||||||
|
$link.off('click').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
confirmStop();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
} else if (href.indexOf('modop=custom&a=Reboot') !== -1) {
|
||||||
|
// Prepend sync icon to Restart button
|
||||||
|
$link.html('<i class="fa fa-sync"></i> ' + $link.text().trim());
|
||||||
|
|
||||||
|
$link.off('click').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
confirmRestart();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
} else if (href.indexOf('modop=custom&a=Shutdown') !== -1) {
|
||||||
|
// Prepend power-off icon to Shutdown button
|
||||||
|
$link.html('<i class="fa fa-power-off"></i> ' + $link.text().trim());
|
||||||
|
|
||||||
|
$link.off('click').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
confirmShutdown();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
} else if (href.indexOf('modop=custom&a=VNC') !== -1) {
|
||||||
|
// Prepend desktop icon to VNC button
|
||||||
|
$link.html('<i class="fa fa-desktop"></i> ' + $link.text().trim());
|
||||||
|
// Open VNC in new tab
|
||||||
|
$link.attr('target', '_blank');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<script src="{$WEB_ROOT}/modules/servers/ArkHostHetznerVPS/template/script.js"></script>
|
<script src="{$WEB_ROOT}/modules/servers/ArkHostHetznerVPS/template/script.js"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user