mirror of
https://gitlab.com/ArkHost/WHMCS-ArkHost-HetznerVPS.git
synced 2026-07-24 15:55:53 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26bdabc534 | ||
|
|
815d1a442b | ||
|
|
372bff51eb | ||
|
|
feb2c7c805 | ||
|
|
378b3a0ba3 | ||
|
|
bc9d8a2e1c | ||
|
|
0f96849032 | ||
|
|
cc60ca7103 |
@@ -12,6 +12,7 @@ WHMCS server module for Hetzner Cloud VPS management.
|
|||||||
|
|
||||||
**Advanced**
|
**Advanced**
|
||||||
|
|
||||||
|
- Cloud-init support for automated server configuration
|
||||||
- Floating IP management with reverse DNS
|
- Floating IP management with reverse DNS
|
||||||
- Backup creation and restoration
|
- Backup creation and restoration
|
||||||
- Firewall rule management
|
- Firewall rule management
|
||||||
@@ -38,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`
|
||||||
@@ -55,11 +57,12 @@ WHMCS server module for Hetzner Cloud VPS management.
|
|||||||
|
|
||||||
**Module Settings**
|
**Module Settings**
|
||||||
|
|
||||||
- Server Type: `cx11`, `cx21`, `cx31`, `cx41`, `cx51` , etc.
|
- Server Type: `cx23`, `cx33`, `cx43`, `cx53` (Cost-Optimized), `cpx11`, `cpx21`, `cax11`, `ccx13`, etc.
|
||||||
- Operating System: `ubuntu-20.04`, `debian-11`, etc.
|
- Operating System: `ubuntu-22.04`, `debian-11`, `rocky-9`, etc.
|
||||||
- Datacenter: `fsn1`, `nbg1`, `hel1`, `ash`, `hil`
|
- Datacenter: `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
|
||||||
|
|
||||||
**Custom Field (Required)**
|
**Custom Field (Required)**
|
||||||
|
|
||||||
@@ -86,36 +89,76 @@ Create in Setup → Products/Services → Configurable Options:
|
|||||||
- Option Name: `datacenter`
|
- Option Name: `datacenter`
|
||||||
- Option Type: `Dropdown`
|
- Option Type: `Dropdown`
|
||||||
- Options:
|
- Options:
|
||||||
- `fsn1|Falkenstein, Germany`
|
- `fsn1-dc14|Falkenstein, Germany`
|
||||||
- `nbg1|Nuremberg, Germany`
|
- `nbg1-dc3|Nuremberg, Germany`
|
||||||
- `hel1|Helsinki, Finland`
|
- `hel1-dc2|Helsinki, Finland`
|
||||||
- `ash|Ashburn, USA`
|
- `ash-dc1|Ashburn, USA`
|
||||||
- `hil|Hillsboro, USA`
|
- `hil-dc1|Hillsboro, USA`
|
||||||
|
- `sin-dc1|Singapore, Singapore`
|
||||||
|
|
||||||
**3\. Server Type**
|
**3\. Server Type**
|
||||||
|
|
||||||
- Option Name: `planid`
|
- Option Name: `planid`
|
||||||
- Option Type: `Dropdown`
|
- Option Type: `Dropdown`
|
||||||
- Options:
|
- Options:
|
||||||
- `cx11|CX11 (1 vCPU, 2GB RAM, 20GB SSD)`
|
- `cx23|CX23 - 2 vCPU, 4 GB RAM, 40 GB Disk, shared (Cost-Optimized)`
|
||||||
- `cx21|CX21 (2 vCPU, 4GB RAM, 40GB SSD)`
|
- `cx33|CX33 - 4 vCPU, 8 GB RAM, 80 GB Disk, shared (Cost-Optimized)`
|
||||||
- `cx31|CX31 (2 vCPU, 8GB RAM, 80GB SSD)`
|
- `cx43|CX43 - 8 vCPU, 16 GB RAM, 160 GB Disk, shared (Cost-Optimized)`
|
||||||
- `cx41|CX41 (4 vCPU, 16GB RAM, 160GB SSD)`
|
- `cx53|CX53 - 16 vCPU, 32 GB RAM, 320 GB Disk, shared (Cost-Optimized)`
|
||||||
- `cx51|CX51 (8 vCPU, 32GB RAM, 240GB SSD)`
|
- `cpx11|CPX 11 - 2 vCPU, 2 GB RAM, 40 GB Disk, shared`
|
||||||
|
- `cpx21|CPX 21 - 3 vCPU, 4 GB RAM, 80 GB Disk, shared`
|
||||||
|
- `cpx31|CPX 31 - 4 vCPU, 8 GB RAM, 160 GB Disk, shared`
|
||||||
|
- `cpx41|CPX 41 - 8 vCPU, 16 GB RAM, 240 GB Disk, shared`
|
||||||
|
- `cpx51|CPX 51 - 16 vCPU, 32 GB RAM, 360 GB Disk, shared`
|
||||||
|
- `cax11|CAX11 - 2 vCPU, 4 GB RAM, 40 GB Disk, shared`
|
||||||
|
- `cax21|CAX21 - 4 vCPU, 8 GB RAM, 80 GB Disk, shared`
|
||||||
|
- `cax31|CAX31 - 8 vCPU, 16 GB RAM, 160 GB Disk, shared`
|
||||||
|
- `cax41|CAX41 - 16 vCPU, 32 GB RAM, 320 GB Disk, shared`
|
||||||
|
- `ccx13|CCX13 Dedicated CPU - 2 vCPU, 8 GB RAM, 80 GB Disk, dedicated`
|
||||||
|
- `ccx23|CCX23 Dedicated CPU - 4 vCPU, 16 GB RAM, 160 GB Disk, dedicated`
|
||||||
|
- `ccx33|CCX33 Dedicated CPU - 8 vCPU, 32 GB RAM, 240 GB Disk, dedicated`
|
||||||
|
- `ccx43|CCX43 Dedicated CPU - 16 vCPU, 64 GB RAM, 360 GB Disk, dedicated`
|
||||||
|
- `ccx53|CCX53 Dedicated CPU - 32 vCPU, 128 GB RAM, 600 GB Disk, dedicated`
|
||||||
|
- `ccx63|CCX63 Dedicated CPU - 48 vCPU, 192 GB RAM, 960 GB Disk, dedicated`
|
||||||
|
- `cx22|CX22 - 2 vCPU, 4 GB RAM, 40 GB Disk, shared`
|
||||||
|
- `cx32|CX32 - 4 vCPU, 8 GB RAM, 80 GB Disk, shared`
|
||||||
|
- `cx42|CX42 - 8 vCPU, 16 GB RAM, 160 GB Disk, shared`
|
||||||
|
- `cx52|CX52 - 16 vCPU, 32 GB RAM, 320 GB Disk, shared`
|
||||||
|
|
||||||
|
|
||||||
**4\. Operating System**
|
**4\. Operating System**
|
||||||
|
|
||||||
- Option Name: `osid`
|
- Option Name: `osid`
|
||||||
- Option Type: `Dropdown`
|
- Option Type: `Dropdown`
|
||||||
- Options:
|
- Options:
|
||||||
- `ubuntu-20.04|Ubuntu 20.04 LTS`
|
- `debian-11|Debian 11`
|
||||||
- `ubuntu-22.04|Ubuntu 22.04 LTS`
|
- `debian-12|Debian 12`
|
||||||
- `debian-11|Debian 11`
|
- `debian-13|Debian 13`
|
||||||
- `debian-12|Debian 12`
|
- `ubuntu-22.04|Ubuntu 22.04`
|
||||||
- `centos-stream-8|CentOS Stream 8`
|
- `ubuntu-24.04|Ubuntu 24.04`
|
||||||
- `centos-stream-9|CentOS Stream 9`
|
- `rocky-8|Rocky Linux 8`
|
||||||
- `rocky-8|Rocky Linux 8`
|
- `rocky-9|Rocky Linux 9`
|
||||||
- `rocky-9|Rocky Linux 9`
|
- `rocky-10|Rocky Linux 10`
|
||||||
|
- `alma-8|AlmaLinux 8`
|
||||||
|
- `alma-9|AlmaLinux 9`
|
||||||
|
- `alma-10|AlmaLinux 10`
|
||||||
|
- `centos-stream-9|CentOS Stream 9`
|
||||||
|
- `centos-stream-10|CentOS Stream 10`
|
||||||
|
- `opensuse-15|openSUSE 15`
|
||||||
|
- `fedora-41|Fedora 41`
|
||||||
|
- `fedora-42|Fedora 42`
|
||||||
|
- `docker-ce|Docker CE`
|
||||||
|
- `lamp|LAMP Stack`
|
||||||
|
- `wordpress|WordPress`
|
||||||
|
- `nextcloud|Nextcloud`
|
||||||
|
- `gitlab|GitLab`
|
||||||
|
- `jitsi|Jitsi`
|
||||||
|
- `wireguard|WireGuard`
|
||||||
|
- `prometheus-grafana|Prometheus + Grafana`
|
||||||
|
- `owncast|Owncast`
|
||||||
|
- `photoprism|PhotoPrism`
|
||||||
|
- `rustdesk|RustDesk`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -149,6 +192,102 @@ This ensures proper key isolation and security for each customer.
|
|||||||
|
|
||||||
We're exploring secure implementations using cloud-init or per-server user data to enable SSH key management while maintaining proper isolation.
|
We're exploring secure implementations using cloud-init or per-server user data to enable SSH key management while maintaining proper isolation.
|
||||||
|
|
||||||
|
## Cloud-Init Support
|
||||||
|
|
||||||
|
Cloud-init allows automatic server configuration during first boot. Unlike project-level SSH keys, cloud-init user_data is per-server, ensuring proper isolation in multi-tenant environments.
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
|
||||||
|
1. Navigate to Setup → Products/Services → Products/Services
|
||||||
|
2. Edit your product → Module Settings tab
|
||||||
|
3. Find "Cloud-Init YAML (Optional)" textarea
|
||||||
|
4. Enter your cloud-init configuration in YAML format
|
||||||
|
5. Leave empty to skip cloud-init
|
||||||
|
|
||||||
|
**Common Use Cases:**
|
||||||
|
|
||||||
|
**1. Change APT Mirrors** (original use case - avoid Hetzner mirrors):
|
||||||
|
```yaml
|
||||||
|
#cloud-config
|
||||||
|
apt:
|
||||||
|
primary:
|
||||||
|
- arches: [default]
|
||||||
|
uri: http://de.archive.ubuntu.com/ubuntu/
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Add SSH Keys** (secure alternative to project-level keys):
|
||||||
|
```yaml
|
||||||
|
#cloud-config
|
||||||
|
users:
|
||||||
|
- name: admin
|
||||||
|
ssh_authorized_keys:
|
||||||
|
- ssh-rsa AAAAB3NzaC1yc2E... user@laptop
|
||||||
|
sudo: ALL=(ALL) NOPASSWD:ALL
|
||||||
|
shell: /bin/bash
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Install Docker:**
|
||||||
|
```yaml
|
||||||
|
#cloud-config
|
||||||
|
packages:
|
||||||
|
- docker.io
|
||||||
|
- docker-compose
|
||||||
|
|
||||||
|
runcmd:
|
||||||
|
- systemctl enable docker
|
||||||
|
- systemctl start docker
|
||||||
|
```
|
||||||
|
|
||||||
|
**4. Security Hardening:**
|
||||||
|
```yaml
|
||||||
|
#cloud-config
|
||||||
|
packages:
|
||||||
|
- fail2ban
|
||||||
|
- ufw
|
||||||
|
|
||||||
|
runcmd:
|
||||||
|
- ufw default deny incoming
|
||||||
|
- ufw default allow outgoing
|
||||||
|
- ufw allow 22/tcp
|
||||||
|
- ufw --force enable
|
||||||
|
- systemctl enable fail2ban
|
||||||
|
- systemctl start fail2ban
|
||||||
|
```
|
||||||
|
|
||||||
|
**5. Combined Configuration:**
|
||||||
|
```yaml
|
||||||
|
#cloud-config
|
||||||
|
apt:
|
||||||
|
primary:
|
||||||
|
- arches: [default]
|
||||||
|
uri: http://mirror.example.com/ubuntu/
|
||||||
|
|
||||||
|
users:
|
||||||
|
- name: admin
|
||||||
|
ssh_authorized_keys:
|
||||||
|
- ssh-rsa AAAAB3... admin@company
|
||||||
|
sudo: ALL=(ALL) NOPASSWD:ALL
|
||||||
|
|
||||||
|
packages:
|
||||||
|
- fail2ban
|
||||||
|
- ufw
|
||||||
|
- docker.io
|
||||||
|
|
||||||
|
runcmd:
|
||||||
|
- ufw allow 22/tcp
|
||||||
|
- ufw --force enable
|
||||||
|
- systemctl enable docker fail2ban
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important Notes:**
|
||||||
|
|
||||||
|
- YAML must be valid syntax (use a YAML validator if unsure)
|
||||||
|
- If you don't start with `#cloud-config`, the module will add it automatically
|
||||||
|
- Cloud-init runs ONCE on first boot only
|
||||||
|
- Invalid YAML will cause provisioning to fail silently
|
||||||
|
- Maximum size: 32 KiB (Hetzner API limit)
|
||||||
|
- Documentation: https://docs.hetzner.cloud/#servers-create-a-server
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||

|

|
||||||

|

|
||||||
@@ -167,9 +306,27 @@ We're exploring secure implementations using cloud-init or per-server user data
|
|||||||
|
|
||||||
Enable module debugging: Configuration → System Logs → Module Log
|
Enable module debugging: Configuration → System Logs → Module Log
|
||||||
|
|
||||||
## Support
|
## Support Policy
|
||||||
|
|
||||||
[support@arkhost.com](mailto:support@arkhost.com "mailto:support@arkhost.com")
|
This is free, open-source software provided AS-IS.
|
||||||
|
|
||||||
|
**NO SUPPORT** is provided except through paid channels:
|
||||||
|
- Premium Support: €100/hour (2 hour minimum)
|
||||||
|
- Email: support@arkhost.com (paid support only)
|
||||||
|
|
||||||
|
### Before Requesting Support
|
||||||
|
- English only
|
||||||
|
- Must provide complete error logs
|
||||||
|
- Must provide steps to reproduce
|
||||||
|
- Vague reports like "doesn't work" will be ignored
|
||||||
|
|
||||||
|
### GitHub Issues
|
||||||
|
- 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
|
||||||
|
|
||||||
@@ -181,6 +338,73 @@ 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")
|
||||||
|
|
||||||
© 2025 ArkHost
|
## Changelog
|
||||||
|
|
||||||
<br>
|
### v1.2.4
|
||||||
|
**Fixed:**
|
||||||
|
- IP address now stored on the service at provisioning (and backfilled for existing services) so it displays in WHMCS instead of showing blank
|
||||||
|
- Service username now set to `root` to match the actual VPS login
|
||||||
|
- Install password now actually removed after the 72-hour window (was staying visible via the eye icon, including for older services that predated expiry tracking)
|
||||||
|
|
||||||
|
### v1.2.3
|
||||||
|
**Added:**
|
||||||
|
- VNC console key combination bar with Ctrl+Alt+Del, modifier keys (Shift, Alt, Ctrl), Tab, Enter, Del, Backspace, Esc, Shift+Up, and Fullscreen toggle
|
||||||
|
|
||||||
|
### v1.2.2
|
||||||
|
- WHMCS v9 compatibility: Updated Smarty template syntax for Smarty v4
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* WHMCS Server Module - Hetzner VPS
|
* WHMCS Server Module - Hetzner VPS
|
||||||
*
|
*
|
||||||
* @package WHMCS
|
* @package WHMCS
|
||||||
* @version 1.0
|
* @version 1.2.4
|
||||||
* @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
|
||||||
@@ -116,12 +116,12 @@ function ArkHostHetznerVPS_API(array $params) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Packages':
|
case 'Packages':
|
||||||
$url .= 'server_types';
|
$url .= 'server_types?per_page=50';
|
||||||
$method = 'GET';
|
$method = 'GET';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Operating Systems':
|
case 'Operating Systems':
|
||||||
$url .= 'images?type=system';
|
$url .= 'images?type=system&per_page=50';
|
||||||
$method = 'GET';
|
$method = 'GET';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -177,6 +177,20 @@ function ArkHostHetznerVPS_API(array $params) {
|
|||||||
$data['enable_ipv6'] = false;
|
$data['enable_ipv6'] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle Cloud-Init user_data if provided
|
||||||
|
$cloudInitYaml = ArkHostHetznerVPS_GetOption($params, 'cloud_init_yaml');
|
||||||
|
if ($cloudInitYaml && trim($cloudInitYaml) !== '') {
|
||||||
|
// Basic YAML validation - check if it starts with #cloud-config
|
||||||
|
$trimmedYaml = trim($cloudInitYaml);
|
||||||
|
if (strpos($trimmedYaml, '#cloud-config') !== 0) {
|
||||||
|
// Auto-prepend #cloud-config if missing
|
||||||
|
$cloudInitYaml = "#cloud-config\n" . $cloudInitYaml;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass as plain text - Hetzner API accepts user_data as plain string (max 32KiB)
|
||||||
|
$data['user_data'] = $cloudInitYaml;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle SSH keys from custom field if needed
|
// Handle SSH keys from custom field if needed
|
||||||
// This would need to be implemented with a custom field
|
// This would need to be implemented with a custom field
|
||||||
break;
|
break;
|
||||||
@@ -239,7 +253,7 @@ function ArkHostHetznerVPS_API(array $params) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Operating Systems - Server':
|
case 'Operating Systems - Server':
|
||||||
$url .= 'images?type=system';
|
$url .= 'images?type=system&per_page=50';
|
||||||
$method = 'GET';
|
$method = 'GET';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -331,11 +345,11 @@ function ArkHostHetznerVPS_API(array $params) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Create backup':
|
case 'Create backup':
|
||||||
// Use snapshots
|
// Create manual backup (still type=backup, but with distinct description)
|
||||||
$url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/create_image';
|
$url .= 'servers/' . ArkHostHetznerVPS_GetVPSID($params) . '/actions/create_image';
|
||||||
$method = 'POST';
|
$method = 'POST';
|
||||||
$data = array(
|
$data = array(
|
||||||
'description' => 'Backup created on ' . date('Y-m-d H:i:s'),
|
'description' => 'Manual backup - ' . date('Y-m-d H:i:s'),
|
||||||
'type' => 'backup'
|
'type' => 'backup'
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
@@ -387,11 +401,14 @@ function ArkHostHetznerVPS_API(array $params) {
|
|||||||
case 'Apply Firewall':
|
case 'Apply Firewall':
|
||||||
$url .= 'firewalls/' . $params['firewall_id'] . '/actions/apply_to_resources';
|
$url .= 'firewalls/' . $params['firewall_id'] . '/actions/apply_to_resources';
|
||||||
$method = 'POST';
|
$method = 'POST';
|
||||||
|
$serverId = ArkHostHetznerVPS_GetVPSID($params);
|
||||||
$data = array(
|
$data = array(
|
||||||
'apply_to' => array(
|
'apply_to' => array(
|
||||||
array(
|
array(
|
||||||
'type' => 'server',
|
'type' => 'server',
|
||||||
'server' => ArkHostHetznerVPS_GetVPSID($params)
|
'server' => array(
|
||||||
|
'id' => intval($serverId)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -446,56 +463,152 @@ function ArkHostHetznerVPS_API(array $params) {
|
|||||||
// No firewall attached, create one
|
// No firewall attached, create one
|
||||||
$createParams = $params;
|
$createParams = $params;
|
||||||
$createParams['action'] = 'Create Firewall';
|
$createParams['action'] = 'Create Firewall';
|
||||||
$createParams['name'] = 'firewall-server-' . $serverId;
|
// Simple unique name
|
||||||
|
$createParams['name'] = 'server-' . $serverId . '-' . time();
|
||||||
$createParams['rules'] = array();
|
$createParams['rules'] = array();
|
||||||
|
|
||||||
$createResult = ArkHostHetznerVPS_API($createParams);
|
try {
|
||||||
|
$createResult = ArkHostHetznerVPS_API($createParams);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// If name is still not unique, try with random suffix
|
||||||
|
if (strpos($e->getMessage(), 'uniqueness_error') !== false || strpos($e->getMessage(), 'name is already used') !== false) {
|
||||||
|
$createParams['name'] = 'firewall-server-' . $serverId . '-' . time() . '-' . rand(1000, 9999);
|
||||||
|
$createResult = ArkHostHetznerVPS_API($createParams);
|
||||||
|
} else {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
$firewallId = $createResult['firewall']['id'];
|
$firewallId = $createResult['firewall']['id'];
|
||||||
|
|
||||||
// Attach firewall to server
|
// Attach firewall to server
|
||||||
$attachParams = $params;
|
$attachParams = $params;
|
||||||
$attachParams['action'] = 'Apply Firewall';
|
$attachParams['action'] = 'Apply Firewall';
|
||||||
$attachParams['firewall_id'] = $firewallId;
|
$attachParams['firewall_id'] = $firewallId;
|
||||||
ArkHostHetznerVPS_API($attachParams);
|
$attachResult = ArkHostHetznerVPS_API($attachParams);
|
||||||
|
|
||||||
|
// Wait a moment for the firewall to be applied
|
||||||
|
sleep(2);
|
||||||
|
|
||||||
|
// Verify the firewall was attached
|
||||||
|
$verifyParams = $params;
|
||||||
|
$verifyParams['action'] = 'Server Info';
|
||||||
|
$verifyInfo = ArkHostHetznerVPS_API($verifyParams);
|
||||||
|
|
||||||
|
// Check if firewall is actually attached
|
||||||
|
if (!isset($verifyInfo['server']['public_net']['firewalls']) ||
|
||||||
|
empty($verifyInfo['server']['public_net']['firewalls']) ||
|
||||||
|
$verifyInfo['server']['public_net']['firewalls'][0]['id'] != $firewallId) {
|
||||||
|
// Try to attach again if it failed
|
||||||
|
$attachParams = $params;
|
||||||
|
$attachParams['action'] = 'Apply Firewall';
|
||||||
|
$attachParams['firewall_id'] = $firewallId;
|
||||||
|
ArkHostHetznerVPS_API($attachParams);
|
||||||
|
}
|
||||||
|
|
||||||
$existingRules = array();
|
$existingRules = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new rule to existing rules
|
// Add new rule to existing rules
|
||||||
$newRule = array(
|
// Handle protocol - Hetzner API doesn't accept "ANY", we need to create multiple rules
|
||||||
'direction' => 'in',
|
$protocol = strtolower($params['protocol']);
|
||||||
'protocol' => strtolower($params['protocol']),
|
$rulesToAdd = array();
|
||||||
);
|
|
||||||
|
|
||||||
// Handle source IPs - ensure CIDR notation
|
if ($protocol === 'any') {
|
||||||
if (!empty($params['source']) && $params['source'] !== '0.0.0.0/0') {
|
// Create rules for tcp and udp when "ANY" is selected
|
||||||
$sourceIp = $params['source'];
|
$protocols = array('tcp', 'udp');
|
||||||
// If no CIDR notation, add /32 for IPv4 or /128 for IPv6
|
foreach ($protocols as $proto) {
|
||||||
if (strpos($sourceIp, '/') === false) {
|
$rule = array(
|
||||||
if (filter_var($sourceIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
'direction' => isset($params['direction']) ? $params['direction'] : 'in',
|
||||||
$sourceIp .= '/128';
|
'protocol' => $proto,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle IPs - use source_ips for inbound, destination_ips for outbound
|
||||||
|
$direction = isset($params['direction']) ? $params['direction'] : 'in';
|
||||||
|
if (!empty($params['source']) && $params['source'] !== 'Any') {
|
||||||
|
$sourceIp = trim($params['source']);
|
||||||
|
// If no CIDR notation, add /32 for IPv4 or /128 for IPv6
|
||||||
|
if (strpos($sourceIp, '/') === false) {
|
||||||
|
if (filter_var($sourceIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||||
|
$sourceIp .= '/128';
|
||||||
|
} else {
|
||||||
|
$sourceIp .= '/32';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($direction === 'out') {
|
||||||
|
$rule['destination_ips'] = array($sourceIp);
|
||||||
|
$rule['source_ips'] = array();
|
||||||
|
} else {
|
||||||
|
$rule['source_ips'] = array($sourceIp);
|
||||||
|
$rule['destination_ips'] = array();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$sourceIp .= '/32';
|
// For "Any" or empty, use 0.0.0.0/0 and ::/0 for all IPs
|
||||||
|
if ($direction === 'out') {
|
||||||
|
$rule['destination_ips'] = array('0.0.0.0/0', '::/0');
|
||||||
|
$rule['source_ips'] = array();
|
||||||
|
} else {
|
||||||
|
$rule['source_ips'] = array('0.0.0.0/0', '::/0');
|
||||||
|
$rule['destination_ips'] = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle port - Hetzner expects port as a string
|
||||||
|
if (!empty($params['port'])) {
|
||||||
|
$rule['port'] = strval($params['port']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rulesToAdd[] = $rule;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Single protocol rule
|
||||||
|
$newRule = array(
|
||||||
|
'direction' => isset($params['direction']) ? $params['direction'] : 'in',
|
||||||
|
'protocol' => $protocol,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle IPs - use source_ips for inbound, destination_ips for outbound
|
||||||
|
$direction = isset($params['direction']) ? $params['direction'] : 'in';
|
||||||
|
if (!empty($params['source']) && $params['source'] !== 'Any') {
|
||||||
|
$sourceIp = trim($params['source']);
|
||||||
|
// If no CIDR notation, add /32 for IPv4 or /128 for IPv6
|
||||||
|
if (strpos($sourceIp, '/') === false) {
|
||||||
|
if (filter_var($sourceIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||||
|
$sourceIp .= '/128';
|
||||||
|
} else {
|
||||||
|
$sourceIp .= '/32';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($direction === 'out') {
|
||||||
|
$newRule['destination_ips'] = array($sourceIp);
|
||||||
|
$newRule['source_ips'] = array();
|
||||||
|
} else {
|
||||||
|
$newRule['source_ips'] = array($sourceIp);
|
||||||
|
$newRule['destination_ips'] = array();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// For "Any" or empty, use 0.0.0.0/0 and ::/0 for all IPs
|
||||||
|
if ($direction === 'out') {
|
||||||
|
$newRule['destination_ips'] = array('0.0.0.0/0', '::/0');
|
||||||
|
$newRule['source_ips'] = array();
|
||||||
|
} else {
|
||||||
|
$newRule['source_ips'] = array('0.0.0.0/0', '::/0');
|
||||||
|
$newRule['destination_ips'] = array();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$newRule['source_ips'] = array($sourceIp);
|
|
||||||
} else {
|
// Handle port - Hetzner expects port as a string
|
||||||
$newRule['source_ips'] = array();
|
if (!empty($params['port']) && $protocol !== 'icmp') {
|
||||||
|
$newRule['port'] = strval($params['port']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rulesToAdd[] = $newRule;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle port
|
// Add the new rules to existing rules
|
||||||
if (!empty($params['port']) && $params['protocol'] !== 'icmp') {
|
// Note: Hetzner only supports ACCEPT rules for inbound traffic
|
||||||
$newRule['port'] = $params['port'];
|
// The default policy is DROP for non-matched traffic
|
||||||
}
|
foreach ($rulesToAdd as $ruleToAdd) {
|
||||||
|
$existingRules[] = $ruleToAdd;
|
||||||
// Add description if provided
|
|
||||||
if (!empty($params['note'])) {
|
|
||||||
$newRule['description'] = $params['note'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only add ACCEPT rules (Hetzner default is DROP for non-matched)
|
|
||||||
if ($params['firewallAction'] === 'ACCEPT') {
|
|
||||||
$existingRules[] = $newRule;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update firewall with new rules
|
// Update firewall with new rules
|
||||||
@@ -535,15 +648,21 @@ function ArkHostHetznerVPS_API(array $params) {
|
|||||||
|
|
||||||
// Remove the rule with matching ID (we use index as ID)
|
// Remove the rule with matching ID (we use index as ID)
|
||||||
$ruleIdToDelete = $params['rule_id'];
|
$ruleIdToDelete = $params['rule_id'];
|
||||||
$index = 0;
|
$inIndex = 0;
|
||||||
|
$outIndex = 0;
|
||||||
foreach ($existingRules as $rule) {
|
foreach ($existingRules as $rule) {
|
||||||
if ($rule['direction'] === 'in') {
|
if ($rule['direction'] === 'in') {
|
||||||
if ('fw_' . $firewallId . '_' . $index != $ruleIdToDelete) {
|
if ('fw_' . $firewallId . '_in_' . $inIndex != $ruleIdToDelete) {
|
||||||
$newRules[] = $rule;
|
$newRules[] = $rule;
|
||||||
}
|
}
|
||||||
$index++;
|
$inIndex++;
|
||||||
|
} else if ($rule['direction'] === 'out') {
|
||||||
|
if ('fw_' . $firewallId . '_out_' . $outIndex != $ruleIdToDelete) {
|
||||||
|
$newRules[] = $rule;
|
||||||
|
}
|
||||||
|
$outIndex++;
|
||||||
} else {
|
} else {
|
||||||
// Keep outbound rules
|
// Keep other rules
|
||||||
$newRules[] = $rule;
|
$newRules[] = $rule;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -883,6 +1002,13 @@ function ArkHostHetznerVPS_ConfigOptions() {
|
|||||||
'Description' => 'Automatically create a floating IP when provisioning this server (additional cost - billed by Hetzner).',
|
'Description' => 'Automatically create a floating IP when provisioning this server (additional cost - billed by Hetzner).',
|
||||||
'Type' => 'yesno',
|
'Type' => 'yesno',
|
||||||
),
|
),
|
||||||
|
'cloud_init_yaml' => array(
|
||||||
|
'FriendlyName' => 'Cloud-Init YAML (Optional)',
|
||||||
|
'Description' => 'Custom cloud-init configuration in YAML format (max 32KiB). Passed as user_data to Hetzner API during server creation. Leave empty to skip cloud-init. <a href="https://docs.hetzner.cloud/#servers-create-a-server" target="_blank">Documentation</a>',
|
||||||
|
'Type' => 'textarea',
|
||||||
|
'Rows' => '10',
|
||||||
|
'Cols' => '60',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -1050,11 +1176,33 @@ function ArkHostHetznerVPS_CreateAccount(array $params) {
|
|||||||
'ArkHostHetznerVPS|VPS ID' => $create['server']['id'],
|
'ArkHostHetznerVPS|VPS ID' => $create['server']['id'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Store the primary IP address and login username so they display in WHMCS.
|
||||||
|
// Without this, the admin service page shows a blank Dedicated IP and the
|
||||||
|
// auto-generated order username instead of the real VPS login ("root").
|
||||||
|
$primaryIp = '';
|
||||||
|
if (!empty($create['server']['public_net']['ipv4']['ip'])) {
|
||||||
|
$primaryIp = $create['server']['public_net']['ipv4']['ip'];
|
||||||
|
} elseif (!empty($create['server']['public_net']['ipv6']['ip'])) {
|
||||||
|
// IPv6-only servers report the network as <addr>/64; strip the prefix length.
|
||||||
|
$primaryIp = explode('/', $create['server']['public_net']['ipv6']['ip'])[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
$hostingUpdate = array('username' => 'root');
|
||||||
|
if ($primaryIp !== '') {
|
||||||
|
$hostingUpdate['dedicatedip'] = $primaryIp;
|
||||||
|
}
|
||||||
|
Capsule::table('tblhosting')->where('id', $params['serviceid'])->update($hostingUpdate);
|
||||||
|
|
||||||
// Store the root password if provided
|
// Store the root password if provided
|
||||||
if (isset($create['root_password'])) {
|
if (isset($create['root_password'])) {
|
||||||
Capsule::table('tblhosting')->where('id', $params['serviceid'])->update([
|
Capsule::table('tblhosting')->where('id', $params['serviceid'])->update([
|
||||||
'password' => encrypt($create['root_password'])
|
'password' => encrypt($create['root_password'])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Save the timestamp when password was set for expiration tracking (72 hours)
|
||||||
|
$params['model']->serviceProperties->save([
|
||||||
|
'ArkHostHetznerVPS|Password Set Time' => time()
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle floating IP creation if requested via Configurable Options or Module Settings
|
// Handle floating IP creation if requested via Configurable Options or Module Settings
|
||||||
@@ -1222,6 +1370,10 @@ function ArkHostHetznerVPS_VNC(array $params) {
|
|||||||
<style>
|
<style>
|
||||||
body { margin: 0; background-color: #000; height: 100vh; display: flex; flex-direction: column; font-family: Arial, sans-serif; }
|
body { margin: 0; background-color: #000; height: 100vh; display: flex; flex-direction: column; font-family: Arial, sans-serif; }
|
||||||
#top_bar { background-color: #333; color: white; padding: 10px; text-align: center; font-size: 14px; }
|
#top_bar { background-color: #333; color: white; padding: 10px; text-align: center; font-size: 14px; }
|
||||||
|
#key_bar { background-color: #2a2a2a; padding: 8px 10px; text-align: center; display: flex; gap: 6px; justify-content: center; flex-wrap: wrap; }
|
||||||
|
#key_bar button { background-color: #555; color: white; border: 1px solid #777; padding: 5px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; font-family: Arial, sans-serif; }
|
||||||
|
#key_bar button:hover { background-color: #777; }
|
||||||
|
#key_bar button:active { background-color: #999; }
|
||||||
#status { text-align: center; padding: 10px; background: #555; color: white; }
|
#status { text-align: center; padding: 10px; background: #555; color: white; }
|
||||||
#screen { flex: 1; display: flex; width: 100%; height: 100%; background: #000; position: relative; }
|
#screen { flex: 1; display: flex; width: 100%; height: 100%; background: #000; position: relative; }
|
||||||
#screen canvas { margin: auto; }
|
#screen canvas { margin: auto; }
|
||||||
@@ -1238,6 +1390,19 @@ function ArkHostHetznerVPS_VNC(array $params) {
|
|||||||
Password: <span style="font-family: monospace; background: #444; padding: 2px 6px; border-radius: 3px;">' . htmlspecialchars($vncPassword) . '</span>
|
Password: <span style="font-family: monospace; background: #444; padding: 2px 6px; border-radius: 3px;">' . htmlspecialchars($vncPassword) . '</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="key_bar">
|
||||||
|
<button onclick="sendCtrlAltDel()">Ctrl + Alt + Del</button>
|
||||||
|
<button onclick="sendKey(0xFFE3, \'ShiftLeft\')">Shift</button>
|
||||||
|
<button onclick="sendKey(0xFFE9, \'AltLeft\')">Alt</button>
|
||||||
|
<button onclick="sendKey(0xFFE1, \'ControlLeft\')">Ctrl</button>
|
||||||
|
<button onclick="sendKey(0xFF09, \'Tab\')">Tab</button>
|
||||||
|
<button onclick="sendKey(0xFF0D, \'Enter\')">Enter</button>
|
||||||
|
<button onclick="sendKey(0xFFFF, \'Delete\')">Del</button>
|
||||||
|
<button onclick="sendKey(0xFF08, \'Backspace\')">Backspace</button>
|
||||||
|
<button onclick="sendKey(0xFF1B, \'Escape\')">Esc</button>
|
||||||
|
<button onclick="sendKey(0xFFE3, \'ShiftLeft\'); sendKey(0xFF52, \'ArrowUp\'); sendKey(0xFFE3, \'ShiftLeft\', false);">Shift+Up</button>
|
||||||
|
<button onclick="toggleFullscreen()">Fullscreen</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="status">Initializing VNC Client...</div>
|
<div id="status">Initializing VNC Client...</div>
|
||||||
<div id="screen"></div>
|
<div id="screen"></div>
|
||||||
@@ -1262,6 +1427,33 @@ function ArkHostHetznerVPS_VNC(array $params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.sendCtrlAltDel = function sendCtrlAltDel() {
|
||||||
|
if (rfb) {
|
||||||
|
rfb.sendCtrlAltDel();
|
||||||
|
rfb.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.sendKey = function sendKey(keysym, code, down) {
|
||||||
|
if (rfb) {
|
||||||
|
if (down === undefined) {
|
||||||
|
rfb.sendKey(keysym, code, true);
|
||||||
|
rfb.sendKey(keysym, code, false);
|
||||||
|
} else {
|
||||||
|
rfb.sendKey(keysym, code, down);
|
||||||
|
}
|
||||||
|
rfb.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.toggleFullscreen = function toggleFullscreen() {
|
||||||
|
if (!document.fullscreenElement) {
|
||||||
|
document.documentElement.requestFullscreen();
|
||||||
|
} else {
|
||||||
|
document.exitFullscreen();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function connect() {
|
function connect() {
|
||||||
updateStatus("Connecting to VNC server...", "warning");
|
updateStatus("Connecting to VNC server...", "warning");
|
||||||
|
|
||||||
@@ -1384,7 +1576,30 @@ function ArkHostHetznerVPS_AdminLink(array $params) {
|
|||||||
|
|
||||||
if (isset($serverInfo['server'])) {
|
if (isset($serverInfo['server'])) {
|
||||||
$server = $serverInfo['server'];
|
$server = $serverInfo['server'];
|
||||||
return '<i class="fa fa-server"></i> Status: ' . $server['status'] . '<br><i class="fa fa-network-wired"></i> IP: ' . $server['public_net']['ipv4']['ip'];
|
|
||||||
|
// Self-heal: backfill IP/username for services created before this was
|
||||||
|
// stored at provisioning time (and keep the IP current after rebuilds).
|
||||||
|
$primaryIp = '';
|
||||||
|
if (!empty($server['public_net']['ipv4']['ip'])) {
|
||||||
|
$primaryIp = $server['public_net']['ipv4']['ip'];
|
||||||
|
} elseif (!empty($server['public_net']['ipv6']['ip'])) {
|
||||||
|
$primaryIp = explode('/', $server['public_net']['ipv6']['ip'])[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($params['serviceid'])) {
|
||||||
|
$hostingUpdate = array();
|
||||||
|
if ($primaryIp !== '' && ($params['dedicatedip'] ?? '') !== $primaryIp) {
|
||||||
|
$hostingUpdate['dedicatedip'] = $primaryIp;
|
||||||
|
}
|
||||||
|
if (($params['username'] ?? '') !== 'root') {
|
||||||
|
$hostingUpdate['username'] = 'root';
|
||||||
|
}
|
||||||
|
if (!empty($hostingUpdate)) {
|
||||||
|
Capsule::table('tblhosting')->where('id', $params['serviceid'])->update($hostingUpdate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '<i class="fa fa-server"></i> Status: ' . $server['status'] . '<br><i class="fa fa-network-wired"></i> IP: ' . ($primaryIp !== '' ? $primaryIp : 'N/A');
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Server ID: ' . $vpsId;
|
return 'Server ID: ' . $vpsId;
|
||||||
@@ -1398,7 +1613,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)) {
|
||||||
@@ -1409,24 +1624,14 @@ function ArkHostHetznerVPS_ClientAreaAPI(array $params) {
|
|||||||
// Check backup permissions
|
// Check backup permissions
|
||||||
$backupActions = array('Create backup', 'Delete backup', 'Restore backup');
|
$backupActions = array('Create backup', 'Delete backup', 'Restore backup');
|
||||||
if (in_array($action, $backupActions)) {
|
if (in_array($action, $backupActions)) {
|
||||||
// Check if backups are enabled either via module settings or configurable options
|
// Check if backups are enabled in module settings
|
||||||
$backupsEnabled = false;
|
$backupsEnabled = (ArkHostHetznerVPS_GetOption($params, 'backups') === 'on');
|
||||||
|
|
||||||
// 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) {
|
if (!$backupsEnabled) {
|
||||||
return array(
|
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.'
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1657,7 +1862,8 @@ function ArkHostHetznerVPS_ClientAreaAPI(array $params) {
|
|||||||
'name' => $image['description'] ?? $image['name'],
|
'name' => $image['description'] ?? $image['name'],
|
||||||
'created' => $image['created'],
|
'created' => $image['created'],
|
||||||
'size' => isset($image['image_size']) ? round($image['image_size'], 2) : 0, // Already in GB from API
|
'size' => isset($image['image_size']) ? round($image['image_size'], 2) : 0, // Already in GB from API
|
||||||
'type' => $image['type']
|
'type' => $image['type'],
|
||||||
|
'status' => $image['status'] ?? 'available' // Include status from Hetzner API
|
||||||
);
|
);
|
||||||
$backupIndex++;
|
$backupIndex++;
|
||||||
}
|
}
|
||||||
@@ -1711,17 +1917,27 @@ function ArkHostHetznerVPS_ClientAreaAPI(array $params) {
|
|||||||
$firewallResult = ArkHostHetznerVPS_API($firewallParams);
|
$firewallResult = ArkHostHetznerVPS_API($firewallParams);
|
||||||
|
|
||||||
if (isset($firewallResult['firewall']['rules'])) {
|
if (isset($firewallResult['firewall']['rules'])) {
|
||||||
|
$inIndex = 0;
|
||||||
|
$outIndex = 0;
|
||||||
foreach ($firewallResult['firewall']['rules'] as $rule) {
|
foreach ($firewallResult['firewall']['rules'] as $rule) {
|
||||||
// Process inbound rules
|
// Process both inbound and outbound rules
|
||||||
if ($rule['direction'] === 'in') {
|
if ($rule['direction'] === 'in' || $rule['direction'] === 'out') {
|
||||||
// Handle source IPs
|
// Handle IPs based on direction
|
||||||
$sourceIps = '0.0.0.0/0'; // Default when no source IPs specified
|
$ips = '0.0.0.0/0'; // Default when no IPs specified
|
||||||
if (isset($rule['source_ips']) && is_array($rule['source_ips'])) {
|
if ($rule['direction'] === 'out') {
|
||||||
if (!empty($rule['source_ips'])) {
|
// For outbound rules, use destination_ips
|
||||||
// Join multiple source IPs with comma
|
if (isset($rule['destination_ips']) && is_array($rule['destination_ips'])) {
|
||||||
$sourceIps = implode(', ', $rule['source_ips']);
|
if (!empty($rule['destination_ips'])) {
|
||||||
|
$ips = implode(', ', $rule['destination_ips']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// For inbound rules, use source_ips
|
||||||
|
if (isset($rule['source_ips']) && is_array($rule['source_ips'])) {
|
||||||
|
if (!empty($rule['source_ips'])) {
|
||||||
|
$ips = implode(', ', $rule['source_ips']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// If source_ips is empty array, it means all sources are allowed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle port range or single port
|
// Handle port range or single port
|
||||||
@@ -1734,21 +1950,23 @@ function ArkHostHetznerVPS_ClientAreaAPI(array $params) {
|
|||||||
$port = 'Any';
|
$port = 'Any';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get description or use firewall name
|
// Generate ID based on direction and index
|
||||||
$description = '';
|
$ruleId = 'fw_' . $firewallId . '_' . $rule['direction'] . '_';
|
||||||
if (isset($rule['description']) && !empty($rule['description'])) {
|
if ($rule['direction'] === 'in') {
|
||||||
$description = $rule['description'];
|
$ruleId .= $inIndex;
|
||||||
|
$inIndex++;
|
||||||
} else {
|
} else {
|
||||||
$description = 'Firewall: ' . (isset($firewallResult['firewall']['name']) ? $firewallResult['firewall']['name'] : $firewallId);
|
$ruleId .= $outIndex;
|
||||||
|
$outIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
$rules[] = array(
|
$rules[] = array(
|
||||||
'id' => 'fw_' . $firewallId . '_' . count($rules),
|
'id' => $ruleId,
|
||||||
|
'direction' => $rule['direction'],
|
||||||
'action' => 'ACCEPT',
|
'action' => 'ACCEPT',
|
||||||
'protocol' => strtoupper($rule['protocol']),
|
'protocol' => strtoupper($rule['protocol']),
|
||||||
'port' => $port,
|
'port' => $port,
|
||||||
'source' => $sourceIps,
|
'source' => $ips
|
||||||
'note' => $description
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1767,52 +1985,13 @@ function ArkHostHetznerVPS_ClientAreaAPI(array $params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (empty($rules)) {
|
if (empty($rules)) {
|
||||||
$results['message'] = 'This server has ' . count($result['server']['firewall_ids']) . ' firewall(s) attached but no inbound rules configured.';
|
$results['message'] = 'This server has ' . count($firewallIds) . ' firewall(s) attached but no inbound rules configured.';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No firewalls attached - show default rules indicating all traffic is allowed
|
// No firewalls attached - don't show any rules
|
||||||
$rules[] = array(
|
$results['message'] = 'No firewall attached to this server. All traffic is allowed by default. Create a firewall rule to enable protection.';
|
||||||
'id' => '1',
|
// Return empty rules array
|
||||||
'action' => 'ACCEPT',
|
$rules = array();
|
||||||
'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
|
// Return rules with numeric keys for JavaScript compatibility
|
||||||
@@ -1827,6 +2006,11 @@ function ArkHostHetznerVPS_ClientAreaAPI(array $params) {
|
|||||||
->where('id', $params['serviceid'])
|
->where('id', $params['serviceid'])
|
||||||
->update(['password' => encrypt($result['root_password'])]);
|
->update(['password' => encrypt($result['root_password'])]);
|
||||||
|
|
||||||
|
// Save the timestamp when password was set for expiration tracking (72 hours)
|
||||||
|
$params['model']->serviceProperties->save([
|
||||||
|
'ArkHostHetznerVPS|Password Set Time' => time()
|
||||||
|
]);
|
||||||
|
|
||||||
$results['root_password'] = $result['root_password'];
|
$results['root_password'] = $result['root_password'];
|
||||||
$results['message'] = 'Server rebuild initiated. New root password has been saved.';
|
$results['message'] = 'Server rebuild initiated. New root password has been saved.';
|
||||||
} else {
|
} else {
|
||||||
@@ -1863,6 +2047,21 @@ function ArkHostHetznerVPS_ClientAreaAPI(array $params) {
|
|||||||
$results['result'] = 'error';
|
$results['result'] = 'error';
|
||||||
$results['message'] = 'Invalid floating IP ID for this service';
|
$results['message'] = 'Invalid floating IP ID for this service';
|
||||||
}
|
}
|
||||||
|
} else if ($action === 'Reset root') {
|
||||||
|
// Handle password reset from client area
|
||||||
|
$results = array_merge($results, is_array($result) ? $result : array('data' => $result));
|
||||||
|
|
||||||
|
// Store the new root password and timestamp if provided
|
||||||
|
if (isset($result['root_password'])) {
|
||||||
|
Capsule::table('tblhosting')->where('id', $params['serviceid'])->update([
|
||||||
|
'password' => encrypt($result['root_password'])
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Save the timestamp when password was set for expiration tracking (72 hours)
|
||||||
|
$params['model']->serviceProperties->save([
|
||||||
|
'ArkHostHetznerVPS|Password Set Time' => time()
|
||||||
|
]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$results = array_merge($results, is_array($result) ? $result : array('data' => $result));
|
$results = array_merge($results, is_array($result) ? $result : array('data' => $result));
|
||||||
}
|
}
|
||||||
@@ -1922,6 +2121,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',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2118,14 +2318,29 @@ function ArkHostHetznerVPS_ClientArea(array $params) {
|
|||||||
// Process operating system info for Hetzner
|
// Process operating system info for Hetzner
|
||||||
if (isset($serverInfo['image']) && !empty($operatingSystemsTemp['images'])) {
|
if (isset($serverInfo['image']) && !empty($operatingSystemsTemp['images'])) {
|
||||||
$osId = $serverInfo['image']['name'];
|
$osId = $serverInfo['image']['name'];
|
||||||
$osName = strtolower($serverInfo['image']['description'] ?? $osId);
|
$imageType = $serverInfo['image']['type'] ?? 'system';
|
||||||
|
|
||||||
// Find matching OS in available images
|
// If this is a backup or snapshot image, try to get the actual OS info from os_flavor/os_version
|
||||||
|
if (($imageType === 'backup' || $imageType === 'snapshot') && isset($serverInfo['image']['os_flavor'])) {
|
||||||
|
// Use os_flavor and os_version to construct a meaningful OS name
|
||||||
|
$osName = strtolower($serverInfo['image']['os_flavor']);
|
||||||
|
$displayName = ucfirst($serverInfo['image']['os_flavor']);
|
||||||
|
if (isset($serverInfo['image']['os_version']) && !empty($serverInfo['image']['os_version'])) {
|
||||||
|
$displayName .= ' ' . $serverInfo['image']['os_version'];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$osName = strtolower($serverInfo['image']['description'] ?? $osId);
|
||||||
|
$displayName = $serverInfo['image']['description'] ?? $osId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find matching OS in available images (only for system images)
|
||||||
$currentOS = null;
|
$currentOS = null;
|
||||||
foreach ($operatingSystemsTemp['images'] as $os) {
|
if ($imageType === 'system') {
|
||||||
if ($os['name'] === $osId) {
|
foreach ($operatingSystemsTemp['images'] as $os) {
|
||||||
$currentOS = $os;
|
if ($os['name'] === $osId) {
|
||||||
break;
|
$currentOS = $os;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2166,9 +2381,14 @@ function ArkHostHetznerVPS_ClientArea(array $params) {
|
|||||||
'image' => isset($operatingSystems[$group]) ? $operatingSystems[$group]['image'] : 'data:image/png;base64,' . base64_encode(file_get_contents($dirOS . (in_array($imageFile, $availableOS) ? $imageFile : 'others') . '.png'))
|
'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 {
|
} else {
|
||||||
// Fallback if OS not found - use image info from server
|
// Fallback if OS not found - already have $displayName and $osName from above
|
||||||
$displayName = $serverInfo['image']['description'] ?? $osId;
|
// Just ensure they are set
|
||||||
$osName = strtolower($displayName);
|
if (!isset($displayName)) {
|
||||||
|
$displayName = $serverInfo['image']['description'] ?? $osId;
|
||||||
|
}
|
||||||
|
if (!isset($osName)) {
|
||||||
|
$osName = strtolower($displayName);
|
||||||
|
}
|
||||||
|
|
||||||
// Try to determine OS type from name
|
// Try to determine OS type from name
|
||||||
if (strpos($osName, 'alma') !== false) {
|
if (strpos($osName, 'alma') !== false) {
|
||||||
@@ -2265,12 +2485,54 @@ 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
|
// Add datacenter info - format as "City, Country"
|
||||||
$serverInfo['datacenter'] = isset($serverInfo['datacenter']['description']) ? $serverInfo['datacenter']['description'] : 'N/A';
|
$city = isset($serverInfo['datacenter']['location']['city']) ? $serverInfo['datacenter']['location']['city'] : '';
|
||||||
$serverInfo['location'] = isset($serverInfo['datacenter']['location']['city']) ? $serverInfo['datacenter']['location']['city'] : 'N/A';
|
$country = isset($serverInfo['datacenter']['location']['country']) ? $serverInfo['datacenter']['location']['country'] : '';
|
||||||
|
|
||||||
// For now, always show backups tab - the API will handle permissions
|
if ($city && $country) {
|
||||||
$backupsEnabled = true;
|
$serverInfo['datacenter'] = $city . ', ' . $country;
|
||||||
|
} elseif ($city) {
|
||||||
|
$serverInfo['datacenter'] = $city;
|
||||||
|
} elseif ($country) {
|
||||||
|
$serverInfo['datacenter'] = $country;
|
||||||
|
} else {
|
||||||
|
$serverInfo['datacenter'] = 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
$serverInfo['location'] = $city ?: 'N/A';
|
||||||
|
|
||||||
|
// Get root password with expiration check (72 hours)
|
||||||
|
$passwordSetTime = $params['model']->serviceProperties->get('ArkHostHetznerVPS|Password Set Time');
|
||||||
|
$currentTime = time();
|
||||||
|
$expirationPeriod = 72 * 3600; // 72 hours in seconds
|
||||||
|
|
||||||
|
$hasPassword = !empty($params['password']) && $params['password'] !== 'managed-via-api';
|
||||||
|
|
||||||
|
// Backfill the timestamp for services created before expiry tracking existed.
|
||||||
|
// Without this, a missing timestamp meant the password was shown forever.
|
||||||
|
// The service registration date is the best estimate of when it was set.
|
||||||
|
if ($hasPassword && !$passwordSetTime) {
|
||||||
|
$regdate = Capsule::table('tblhosting')->where('id', $params['serviceid'])->value('regdate');
|
||||||
|
$passwordSetTime = ($regdate && $regdate !== '0000-00-00') ? strtotime($regdate) : $currentTime;
|
||||||
|
$params['model']->serviceProperties->save([
|
||||||
|
'ArkHostHetznerVPS|Password Set Time' => $passwordSetTime
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasPassword && ($currentTime - $passwordSetTime) < $expirationPeriod) {
|
||||||
|
// Password is still within the 72-hour window - WHMCS already decrypts it for us
|
||||||
|
$serverInfo['install_root'] = $params['password'];
|
||||||
|
} else {
|
||||||
|
// 72-hour window has elapsed: actually remove the install password from our
|
||||||
|
// systems so the stored copy matches what the client area promises.
|
||||||
|
if ($hasPassword) {
|
||||||
|
Capsule::table('tblhosting')->where('id', $params['serviceid'])->update(['password' => '']);
|
||||||
|
}
|
||||||
|
$serverInfo['install_root'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if backups are enabled in module settings
|
||||||
|
$backupsEnabled = (ArkHostHetznerVPS_GetOption($params, 'backups') === 'on');
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'templatefile' => 'template/clientarea_direct',
|
'templatefile' => 'template/clientarea_direct',
|
||||||
@@ -2281,6 +2543,7 @@ function ArkHostHetznerVPS_ClientArea(array $params) {
|
|||||||
'token' => generate_token('plain'),
|
'token' => generate_token('plain'),
|
||||||
'ADDONLANG' => ArkHostHetznerVPS_Translation(),
|
'ADDONLANG' => ArkHostHetznerVPS_Translation(),
|
||||||
'backupsEnabled' => $backupsEnabled,
|
'backupsEnabled' => $backupsEnabled,
|
||||||
|
'productName' => $params['productname'] ?? $params['configoption1'] ?? 'VPS',
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} catch (Exception $err) {
|
} catch (Exception $err) {
|
||||||
@@ -2364,6 +2627,11 @@ function ArkHostHetznerVPS_ResetRoot(array $params) {
|
|||||||
Capsule::table('tblhosting')->where('id', $params['serviceid'])->update([
|
Capsule::table('tblhosting')->where('id', $params['serviceid'])->update([
|
||||||
'password' => encrypt($result['root_password'])
|
'password' => encrypt($result['root_password'])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Save the timestamp when password was set for expiration tracking (72 hours)
|
||||||
|
$params['model']->serviceProperties->save([
|
||||||
|
'ArkHostHetznerVPS|Password Set Time' => time()
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
} catch (Exception $err) {
|
} catch (Exception $err) {
|
||||||
ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err);
|
ArkHostHetznerVPS_Error(__FUNCTION__, $params, $err);
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WHMCS Server Module - Hetzner VPS Hooks
|
||||||
|
*
|
||||||
|
* @package WHMCS
|
||||||
|
* @version 1.2.4
|
||||||
|
* @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;
|
||||||
|
});
|
||||||
|
|
||||||
@@ -149,20 +149,11 @@ $_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Herinstalleren';
|
|||||||
|
|
||||||
### Firewall
|
### Firewall
|
||||||
$_ADDONLANG['Settings']['Firewall']['Title'] = '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']['Action'] = 'Actie';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Poort';
|
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Poort';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocol';
|
$_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']['Accept'] = 'Accepteren';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Drop'] = 'Weggooien';
|
$_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
|
### Rescue Mode
|
||||||
$_ADDONLANG['Settings']['Rescue']['Title'] = 'Reddingsmodus';
|
$_ADDONLANG['Settings']['Rescue']['Title'] = 'Reddingsmodus';
|
||||||
@@ -222,11 +213,16 @@ $_ADDONLANG['General']['Colon'] = ': ';
|
|||||||
## Firewall specific
|
## Firewall specific
|
||||||
$_ADDONLANG['Firewall']['CurrentRules'] = 'Huidige Firewall Regels';
|
$_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']['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']['Direction'] = 'Richting';
|
||||||
|
$_ADDONLANG['Firewall']['Action'] = 'Actie';
|
||||||
|
$_ADDONLANG['Firewall']['Protocol'] = 'Protocol';
|
||||||
|
$_ADDONLANG['Firewall']['Port'] = 'Poort';
|
||||||
|
$_ADDONLANG['Firewall']['IPCIDR'] = 'IP/CIDR';
|
||||||
|
$_ADDONLANG['Firewall']['Remove'] = 'Verwijderen';
|
||||||
$_ADDONLANG['Firewall']['ChangesImmediate'] = 'Firewall wijzigingen worden onmiddellijk toegepast. Het is niet nodig om wijzigingen door te voeren.';
|
$_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']['AddNewRule'] = 'Nieuwe Firewall Regel toevoegen';
|
||||||
$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535';
|
$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535';
|
||||||
$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0';
|
$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0';
|
||||||
$_ADDONLANG['Firewall']['DescriptionPlaceholder'] = 'Regel beschrijving';
|
|
||||||
$_ADDONLANG['Firewall']['Accept'] = 'ACCEPTEREN';
|
$_ADDONLANG['Firewall']['Accept'] = 'ACCEPTEREN';
|
||||||
$_ADDONLANG['Firewall']['Drop'] = 'WEGGOOIEN';
|
$_ADDONLANG['Firewall']['Drop'] = 'WEGGOOIEN';
|
||||||
$_ADDONLANG['Firewall']['Info'] = 'INFO';
|
$_ADDONLANG['Firewall']['Info'] = 'INFO';
|
||||||
@@ -234,6 +230,9 @@ $_ADDONLANG['Firewall']['Any'] = 'ELKE';
|
|||||||
$_ADDONLANG['Firewall']['TCP'] = 'TCP';
|
$_ADDONLANG['Firewall']['TCP'] = 'TCP';
|
||||||
$_ADDONLANG['Firewall']['UDP'] = 'UDP';
|
$_ADDONLANG['Firewall']['UDP'] = 'UDP';
|
||||||
$_ADDONLANG['Firewall']['ICMP'] = 'ICMP';
|
$_ADDONLANG['Firewall']['ICMP'] = 'ICMP';
|
||||||
|
$_ADDONLANG['Firewall']['Incoming'] = 'Inkomend';
|
||||||
|
$_ADDONLANG['Firewall']['Outgoing'] = 'Uitgaand';
|
||||||
|
$_ADDONLANG['Settings']['Firewall']['Direction'] = 'Richting';
|
||||||
|
|
||||||
## Reinstall specific
|
## 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.';
|
$_ADDONLANG['Reinstall']['DestroyWarning'] = 'Herbouwen zal alle gegevens op de server vernietigen. Een nieuw root wachtwoord wordt gegenereerd en opgeslagen in uw service account.';
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ $_ADDONLANG['Overview']['ResourceAllocation'] = 'Resource Allocation';
|
|||||||
$_ADDONLANG['Overview']['QuickActions'] = 'Quick Actions';
|
$_ADDONLANG['Overview']['QuickActions'] = 'Quick Actions';
|
||||||
$_ADDONLANG['Overview']['Hostname'] = 'Hostname';
|
$_ADDONLANG['Overview']['Hostname'] = 'Hostname';
|
||||||
$_ADDONLANG['Overview']['Status'] = 'Status';
|
$_ADDONLANG['Overview']['Status'] = 'Status';
|
||||||
$_ADDONLANG['Overview']['OS'] = 'Operating System';
|
$_ADDONLANG['Overview']['OS'] = 'OS';
|
||||||
$_ADDONLANG['Overview']['Location'] = 'Location';
|
$_ADDONLANG['Overview']['Location'] = 'Location';
|
||||||
$_ADDONLANG['Overview']['CPU_Cores'] = 'vCPU Cores';
|
$_ADDONLANG['Overview']['CPU_Cores'] = 'vCPU Cores';
|
||||||
$_ADDONLANG['Overview']['Memory'] = 'Memory';
|
$_ADDONLANG['Overview']['Memory'] = 'Memory';
|
||||||
@@ -143,26 +143,17 @@ $_ADDONLANG['Settings']['Password']['Submit'] = 'Reset Password';
|
|||||||
### Reinstall
|
### Reinstall
|
||||||
$_ADDONLANG['Settings']['Reinstall']['Title'] = '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']['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']['OS'] = 'OS';
|
||||||
$_ADDONLANG['Settings']['Reinstall']['Version'] = 'CHOOSE VERSION';
|
$_ADDONLANG['Settings']['Reinstall']['Version'] = 'CHOOSE VERSION';
|
||||||
$_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Reinstall';
|
$_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Reinstall';
|
||||||
|
|
||||||
### Firewall
|
### Firewall
|
||||||
$_ADDONLANG['Settings']['Firewall']['Title'] = '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']['Action'] = 'Action';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Port';
|
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Port';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocol';
|
$_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']['Accept'] = 'Accept';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Drop'] = 'Drop';
|
$_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
|
### Rescue Mode
|
||||||
$_ADDONLANG['Settings']['Rescue']['Title'] = 'Rescue Mode';
|
$_ADDONLANG['Settings']['Rescue']['Title'] = 'Rescue Mode';
|
||||||
@@ -222,11 +213,16 @@ $_ADDONLANG['General']['Colon'] = ': ';
|
|||||||
## Firewall specific
|
## Firewall specific
|
||||||
$_ADDONLANG['Firewall']['CurrentRules'] = 'Current Firewall Rules';
|
$_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']['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']['Direction'] = 'Direction';
|
||||||
|
$_ADDONLANG['Firewall']['Action'] = 'Action';
|
||||||
|
$_ADDONLANG['Firewall']['Protocol'] = 'Protocol';
|
||||||
|
$_ADDONLANG['Firewall']['Port'] = 'Port';
|
||||||
|
$_ADDONLANG['Firewall']['IPCIDR'] = 'IP/CIDR';
|
||||||
|
$_ADDONLANG['Firewall']['Remove'] = 'Remove';
|
||||||
$_ADDONLANG['Firewall']['ChangesImmediate'] = 'Firewall changes are applied immediately. There is no need to commit changes.';
|
$_ADDONLANG['Firewall']['ChangesImmediate'] = 'Firewall changes are applied immediately. There is no need to commit changes.';
|
||||||
$_ADDONLANG['Firewall']['AddNewRule'] = 'Add New Firewall Rule';
|
$_ADDONLANG['Firewall']['AddNewRule'] = 'Add New Firewall Rule';
|
||||||
$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535';
|
$_ADDONLANG['Firewall']['PortPlaceholder'] = '80 or 8080-8090';
|
||||||
$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0';
|
$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0 or IP/CIDR';
|
||||||
$_ADDONLANG['Firewall']['DescriptionPlaceholder'] = 'Rule description';
|
|
||||||
$_ADDONLANG['Firewall']['Accept'] = 'ACCEPT';
|
$_ADDONLANG['Firewall']['Accept'] = 'ACCEPT';
|
||||||
$_ADDONLANG['Firewall']['Drop'] = 'DROP';
|
$_ADDONLANG['Firewall']['Drop'] = 'DROP';
|
||||||
$_ADDONLANG['Firewall']['Info'] = 'INFO';
|
$_ADDONLANG['Firewall']['Info'] = 'INFO';
|
||||||
@@ -234,6 +230,9 @@ $_ADDONLANG['Firewall']['Any'] = 'ANY';
|
|||||||
$_ADDONLANG['Firewall']['TCP'] = 'TCP';
|
$_ADDONLANG['Firewall']['TCP'] = 'TCP';
|
||||||
$_ADDONLANG['Firewall']['UDP'] = 'UDP';
|
$_ADDONLANG['Firewall']['UDP'] = 'UDP';
|
||||||
$_ADDONLANG['Firewall']['ICMP'] = 'ICMP';
|
$_ADDONLANG['Firewall']['ICMP'] = 'ICMP';
|
||||||
|
$_ADDONLANG['Firewall']['Incoming'] = 'Incoming';
|
||||||
|
$_ADDONLANG['Firewall']['Outgoing'] = 'Outgoing';
|
||||||
|
$_ADDONLANG['Settings']['Firewall']['Direction'] = 'Direction';
|
||||||
|
|
||||||
## Reinstall specific
|
## 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.';
|
$_ADDONLANG['Reinstall']['DestroyWarning'] = 'Rebuilding will destroy all data on the server. A new root password will be generated and saved to your service account.';
|
||||||
|
|||||||
@@ -149,20 +149,11 @@ $_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Réinstaller';
|
|||||||
|
|
||||||
### Firewall
|
### Firewall
|
||||||
$_ADDONLANG['Settings']['Firewall']['Title'] = 'Pare-feu';
|
$_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']['Action'] = 'Action';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Port';
|
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Port';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocole';
|
$_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']['Accept'] = 'Accepter';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Drop'] = 'Rejeter';
|
$_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
|
### Rescue Mode
|
||||||
$_ADDONLANG['Settings']['Rescue']['Title'] = 'Mode de Secours';
|
$_ADDONLANG['Settings']['Rescue']['Title'] = 'Mode de Secours';
|
||||||
@@ -222,11 +213,16 @@ $_ADDONLANG['General']['Colon'] = ' : ';
|
|||||||
## Firewall specific
|
## Firewall specific
|
||||||
$_ADDONLANG['Firewall']['CurrentRules'] = 'Règles Actuelles du Pare-feu';
|
$_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']['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']['Direction'] = 'Direction';
|
||||||
|
$_ADDONLANG['Firewall']['Action'] = 'Action';
|
||||||
|
$_ADDONLANG['Firewall']['Protocol'] = 'Protocole';
|
||||||
|
$_ADDONLANG['Firewall']['Port'] = 'Port';
|
||||||
|
$_ADDONLANG['Firewall']['IPCIDR'] = 'IP/CIDR';
|
||||||
|
$_ADDONLANG['Firewall']['Remove'] = 'Supprimer';
|
||||||
$_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']['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']['AddNewRule'] = 'Ajouter une Nouvelle Règle de Pare-feu';
|
||||||
$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535';
|
$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535';
|
||||||
$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0';
|
$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0';
|
||||||
$_ADDONLANG['Firewall']['DescriptionPlaceholder'] = 'Description de la règle';
|
|
||||||
$_ADDONLANG['Firewall']['Accept'] = 'ACCEPTER';
|
$_ADDONLANG['Firewall']['Accept'] = 'ACCEPTER';
|
||||||
$_ADDONLANG['Firewall']['Drop'] = 'REJETER';
|
$_ADDONLANG['Firewall']['Drop'] = 'REJETER';
|
||||||
$_ADDONLANG['Firewall']['Info'] = 'INFO';
|
$_ADDONLANG['Firewall']['Info'] = 'INFO';
|
||||||
@@ -234,6 +230,9 @@ $_ADDONLANG['Firewall']['Any'] = 'TOUT';
|
|||||||
$_ADDONLANG['Firewall']['TCP'] = 'TCP';
|
$_ADDONLANG['Firewall']['TCP'] = 'TCP';
|
||||||
$_ADDONLANG['Firewall']['UDP'] = 'UDP';
|
$_ADDONLANG['Firewall']['UDP'] = 'UDP';
|
||||||
$_ADDONLANG['Firewall']['ICMP'] = 'ICMP';
|
$_ADDONLANG['Firewall']['ICMP'] = 'ICMP';
|
||||||
|
$_ADDONLANG['Firewall']['Incoming'] = 'Entrant';
|
||||||
|
$_ADDONLANG['Firewall']['Outgoing'] = 'Sortant';
|
||||||
|
$_ADDONLANG['Settings']['Firewall']['Direction'] = 'Direction';
|
||||||
|
|
||||||
## Reinstall specific
|
## 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.';
|
$_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.';
|
||||||
|
|||||||
@@ -149,20 +149,11 @@ $_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Neuinstallieren';
|
|||||||
|
|
||||||
### Firewall
|
### Firewall
|
||||||
$_ADDONLANG['Settings']['Firewall']['Title'] = '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']['Action'] = 'Aktion';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Port';
|
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Port';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protokoll';
|
$_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']['Accept'] = 'Akzeptieren';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Drop'] = 'Verwerfen';
|
$_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
|
### Rescue Mode
|
||||||
$_ADDONLANG['Settings']['Rescue']['Title'] = 'Rettungsmodus';
|
$_ADDONLANG['Settings']['Rescue']['Title'] = 'Rettungsmodus';
|
||||||
@@ -222,11 +213,16 @@ $_ADDONLANG['General']['Colon'] = ': ';
|
|||||||
## Firewall specific
|
## Firewall specific
|
||||||
$_ADDONLANG['Firewall']['CurrentRules'] = 'Aktuelle Firewall-Regeln';
|
$_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']['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']['Direction'] = 'Richtung';
|
||||||
|
$_ADDONLANG['Firewall']['Action'] = 'Aktion';
|
||||||
|
$_ADDONLANG['Firewall']['Protocol'] = 'Protokoll';
|
||||||
|
$_ADDONLANG['Firewall']['Port'] = 'Port';
|
||||||
|
$_ADDONLANG['Firewall']['IPCIDR'] = 'IP/CIDR';
|
||||||
|
$_ADDONLANG['Firewall']['Remove'] = 'Entfernen';
|
||||||
$_ADDONLANG['Firewall']['ChangesImmediate'] = 'Firewall-Änderungen werden sofort angewendet. Es ist nicht notwendig, Änderungen zu übernehmen.';
|
$_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']['AddNewRule'] = 'Neue Firewall-Regel hinzufügen';
|
||||||
$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535';
|
$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535';
|
||||||
$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0';
|
$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0';
|
||||||
$_ADDONLANG['Firewall']['DescriptionPlaceholder'] = 'Regelbeschreibung';
|
|
||||||
$_ADDONLANG['Firewall']['Accept'] = 'AKZEPTIEREN';
|
$_ADDONLANG['Firewall']['Accept'] = 'AKZEPTIEREN';
|
||||||
$_ADDONLANG['Firewall']['Drop'] = 'VERWERFEN';
|
$_ADDONLANG['Firewall']['Drop'] = 'VERWERFEN';
|
||||||
$_ADDONLANG['Firewall']['Info'] = 'INFO';
|
$_ADDONLANG['Firewall']['Info'] = 'INFO';
|
||||||
@@ -234,6 +230,9 @@ $_ADDONLANG['Firewall']['Any'] = 'ALLE';
|
|||||||
$_ADDONLANG['Firewall']['TCP'] = 'TCP';
|
$_ADDONLANG['Firewall']['TCP'] = 'TCP';
|
||||||
$_ADDONLANG['Firewall']['UDP'] = 'UDP';
|
$_ADDONLANG['Firewall']['UDP'] = 'UDP';
|
||||||
$_ADDONLANG['Firewall']['ICMP'] = 'ICMP';
|
$_ADDONLANG['Firewall']['ICMP'] = 'ICMP';
|
||||||
|
$_ADDONLANG['Firewall']['Incoming'] = 'Eingehend';
|
||||||
|
$_ADDONLANG['Firewall']['Outgoing'] = 'Ausgehend';
|
||||||
|
$_ADDONLANG['Settings']['Firewall']['Direction'] = 'Richtung';
|
||||||
|
|
||||||
## Reinstall specific
|
## 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.';
|
$_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.';
|
||||||
|
|||||||
@@ -149,20 +149,11 @@ $_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Reinstalla';
|
|||||||
|
|
||||||
### Firewall
|
### Firewall
|
||||||
$_ADDONLANG['Settings']['Firewall']['Title'] = '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']['Action'] = 'Azione';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Porta';
|
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Porta';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocollo';
|
$_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']['Accept'] = 'Accetta';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Drop'] = 'Rilascia';
|
$_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
|
### Rescue Mode
|
||||||
$_ADDONLANG['Settings']['Rescue']['Title'] = 'Modalità di Recupero';
|
$_ADDONLANG['Settings']['Rescue']['Title'] = 'Modalità di Recupero';
|
||||||
@@ -222,11 +213,16 @@ $_ADDONLANG['General']['Colon'] = ': ';
|
|||||||
## Firewall specific
|
## Firewall specific
|
||||||
$_ADDONLANG['Firewall']['CurrentRules'] = 'Regole Firewall Attuali';
|
$_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']['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']['Direction'] = 'Direzione';
|
||||||
|
$_ADDONLANG['Firewall']['Action'] = 'Azione';
|
||||||
|
$_ADDONLANG['Firewall']['Protocol'] = 'Protocollo';
|
||||||
|
$_ADDONLANG['Firewall']['Port'] = 'Porta';
|
||||||
|
$_ADDONLANG['Firewall']['IPCIDR'] = 'IP/CIDR';
|
||||||
|
$_ADDONLANG['Firewall']['Remove'] = 'Rimuovi';
|
||||||
$_ADDONLANG['Firewall']['ChangesImmediate'] = 'Le modifiche al firewall vengono applicate immediatamente. Non è necessario confermare le modifiche.';
|
$_ADDONLANG['Firewall']['ChangesImmediate'] = 'Le modifiche al firewall vengono applicate immediatamente. Non è necessario confermare le modifiche.';
|
||||||
$_ADDONLANG['Firewall']['AddNewRule'] = 'Aggiungi Nuova Regola Firewall';
|
$_ADDONLANG['Firewall']['AddNewRule'] = 'Aggiungi Nuova Regola Firewall';
|
||||||
$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535';
|
$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535';
|
||||||
$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0';
|
$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0';
|
||||||
$_ADDONLANG['Firewall']['DescriptionPlaceholder'] = 'Descrizione regola';
|
|
||||||
$_ADDONLANG['Firewall']['Accept'] = 'ACCETTA';
|
$_ADDONLANG['Firewall']['Accept'] = 'ACCETTA';
|
||||||
$_ADDONLANG['Firewall']['Drop'] = 'RILASCIA';
|
$_ADDONLANG['Firewall']['Drop'] = 'RILASCIA';
|
||||||
$_ADDONLANG['Firewall']['Info'] = 'INFO';
|
$_ADDONLANG['Firewall']['Info'] = 'INFO';
|
||||||
@@ -234,6 +230,9 @@ $_ADDONLANG['Firewall']['Any'] = 'QUALSIASI';
|
|||||||
$_ADDONLANG['Firewall']['TCP'] = 'TCP';
|
$_ADDONLANG['Firewall']['TCP'] = 'TCP';
|
||||||
$_ADDONLANG['Firewall']['UDP'] = 'UDP';
|
$_ADDONLANG['Firewall']['UDP'] = 'UDP';
|
||||||
$_ADDONLANG['Firewall']['ICMP'] = 'ICMP';
|
$_ADDONLANG['Firewall']['ICMP'] = 'ICMP';
|
||||||
|
$_ADDONLANG['Firewall']['Incoming'] = 'In entrata';
|
||||||
|
$_ADDONLANG['Firewall']['Outgoing'] = 'In uscita';
|
||||||
|
$_ADDONLANG['Settings']['Firewall']['Direction'] = 'Direzione';
|
||||||
|
|
||||||
## Reinstall specific
|
## 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.';
|
$_ADDONLANG['Reinstall']['DestroyWarning'] = 'La ricostruzione distruggerà tutti i dati sul server. Una nuova password root verrà generata e salvata nel tuo account di servizio.';
|
||||||
|
|||||||
@@ -149,20 +149,11 @@ $_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Reinstalar';
|
|||||||
|
|
||||||
### Firewall
|
### Firewall
|
||||||
$_ADDONLANG['Settings']['Firewall']['Title'] = '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']['Action'] = 'Acção';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Porto';
|
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Porto';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocolo';
|
$_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']['Accept'] = 'Aceitar';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Drop'] = 'Descartar';
|
$_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
|
### Rescue Mode
|
||||||
$_ADDONLANG['Settings']['Rescue']['Title'] = 'Modo de Recuperação';
|
$_ADDONLANG['Settings']['Rescue']['Title'] = 'Modo de Recuperação';
|
||||||
@@ -222,11 +213,16 @@ $_ADDONLANG['General']['Colon'] = ': ';
|
|||||||
## Firewall specific
|
## Firewall specific
|
||||||
$_ADDONLANG['Firewall']['CurrentRules'] = 'Regras de Firewall Actuais';
|
$_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']['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']['Direction'] = 'Direção';
|
||||||
|
$_ADDONLANG['Firewall']['Action'] = 'Ação';
|
||||||
|
$_ADDONLANG['Firewall']['Protocol'] = 'Protocolo';
|
||||||
|
$_ADDONLANG['Firewall']['Port'] = 'Porta';
|
||||||
|
$_ADDONLANG['Firewall']['IPCIDR'] = 'IP/CIDR';
|
||||||
|
$_ADDONLANG['Firewall']['Remove'] = 'Remover';
|
||||||
$_ADDONLANG['Firewall']['ChangesImmediate'] = 'As alterações da firewall são aplicadas imediatamente. Não há necessidade de confirmar alterações.';
|
$_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']['AddNewRule'] = 'Adicionar Nova Regra de Firewall';
|
||||||
$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535';
|
$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535';
|
||||||
$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0';
|
$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0';
|
||||||
$_ADDONLANG['Firewall']['DescriptionPlaceholder'] = 'Descrição da regra';
|
|
||||||
$_ADDONLANG['Firewall']['Accept'] = 'ACEITAR';
|
$_ADDONLANG['Firewall']['Accept'] = 'ACEITAR';
|
||||||
$_ADDONLANG['Firewall']['Drop'] = 'DESCARTAR';
|
$_ADDONLANG['Firewall']['Drop'] = 'DESCARTAR';
|
||||||
$_ADDONLANG['Firewall']['Info'] = 'INFO';
|
$_ADDONLANG['Firewall']['Info'] = 'INFO';
|
||||||
@@ -234,6 +230,9 @@ $_ADDONLANG['Firewall']['Any'] = 'QUALQUER';
|
|||||||
$_ADDONLANG['Firewall']['TCP'] = 'TCP';
|
$_ADDONLANG['Firewall']['TCP'] = 'TCP';
|
||||||
$_ADDONLANG['Firewall']['UDP'] = 'UDP';
|
$_ADDONLANG['Firewall']['UDP'] = 'UDP';
|
||||||
$_ADDONLANG['Firewall']['ICMP'] = 'ICMP';
|
$_ADDONLANG['Firewall']['ICMP'] = 'ICMP';
|
||||||
|
$_ADDONLANG['Firewall']['Incoming'] = 'Entrada';
|
||||||
|
$_ADDONLANG['Firewall']['Outgoing'] = 'Saída';
|
||||||
|
$_ADDONLANG['Settings']['Firewall']['Direction'] = 'Direção';
|
||||||
|
|
||||||
## Reinstall specific
|
## 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.';
|
$_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.';
|
||||||
|
|||||||
@@ -149,20 +149,11 @@ $_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Переустановить';
|
|||||||
|
|
||||||
### Firewall
|
### Firewall
|
||||||
$_ADDONLANG['Settings']['Firewall']['Title'] = 'Файервол';
|
$_ADDONLANG['Settings']['Firewall']['Title'] = 'Файервол';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Description'] = 'Правила оцениваются сверху вниз. По умолчанию все разрешено. Файервол доступен только на публичном интерфейсе. Файервол фильтрует только входящий трафик.';
|
|
||||||
$_ADDONLANG['Settings']['Firewall']['Action'] = 'Действие';
|
$_ADDONLANG['Settings']['Firewall']['Action'] = 'Действие';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Порт';
|
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Порт';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Протокол';
|
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Протокол';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Source'] = 'Источник';
|
|
||||||
$_ADDONLANG['Settings']['Firewall']['Note'] = 'Примечание';
|
|
||||||
$_ADDONLANG['Settings']['Firewall']['Actions'] = 'Действия';
|
|
||||||
$_ADDONLANG['Settings']['Firewall']['Accept'] = 'Принять';
|
$_ADDONLANG['Settings']['Firewall']['Accept'] = 'Принять';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Drop'] = 'Отбросить';
|
$_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
|
### Rescue Mode
|
||||||
$_ADDONLANG['Settings']['Rescue']['Title'] = 'Режим восстановления';
|
$_ADDONLANG['Settings']['Rescue']['Title'] = 'Режим восстановления';
|
||||||
@@ -222,11 +213,16 @@ $_ADDONLANG['General']['Colon'] = ': ';
|
|||||||
## Firewall specific
|
## Firewall specific
|
||||||
$_ADDONLANG['Firewall']['CurrentRules'] = 'Текущие правила файервола';
|
$_ADDONLANG['Firewall']['CurrentRules'] = 'Текущие правила файервола';
|
||||||
$_ADDONLANG['Firewall']['ResourcesAttached'] = 'Ресурсы файервола прикреплены к серверам. Если файервол не прикреплен, он будет создан автоматически, когда вы добавите первое правило.';
|
$_ADDONLANG['Firewall']['ResourcesAttached'] = 'Ресурсы файервола прикреплены к серверам. Если файервол не прикреплен, он будет создан автоматически, когда вы добавите первое правило.';
|
||||||
|
$_ADDONLANG['Firewall']['Direction'] = 'Направление';
|
||||||
|
$_ADDONLANG['Firewall']['Action'] = 'Действие';
|
||||||
|
$_ADDONLANG['Firewall']['Protocol'] = 'Протокол';
|
||||||
|
$_ADDONLANG['Firewall']['Port'] = 'Порт';
|
||||||
|
$_ADDONLANG['Firewall']['IPCIDR'] = 'IP/CIDR';
|
||||||
|
$_ADDONLANG['Firewall']['Remove'] = 'Удалить';
|
||||||
$_ADDONLANG['Firewall']['ChangesImmediate'] = 'Изменения файервола применяются немедленно. Нет необходимости подтверждать изменения.';
|
$_ADDONLANG['Firewall']['ChangesImmediate'] = 'Изменения файервола применяются немедленно. Нет необходимости подтверждать изменения.';
|
||||||
$_ADDONLANG['Firewall']['AddNewRule'] = 'Добавить новое правило файервола';
|
$_ADDONLANG['Firewall']['AddNewRule'] = 'Добавить новое правило файервола';
|
||||||
$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535';
|
$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535';
|
||||||
$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0';
|
$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0';
|
||||||
$_ADDONLANG['Firewall']['DescriptionPlaceholder'] = 'Описание правила';
|
|
||||||
$_ADDONLANG['Firewall']['Accept'] = 'ПРИНЯТЬ';
|
$_ADDONLANG['Firewall']['Accept'] = 'ПРИНЯТЬ';
|
||||||
$_ADDONLANG['Firewall']['Drop'] = 'ОТБРОСИТЬ';
|
$_ADDONLANG['Firewall']['Drop'] = 'ОТБРОСИТЬ';
|
||||||
$_ADDONLANG['Firewall']['Info'] = 'ИНФО';
|
$_ADDONLANG['Firewall']['Info'] = 'ИНФО';
|
||||||
@@ -234,6 +230,9 @@ $_ADDONLANG['Firewall']['Any'] = 'ЛЮБОЙ';
|
|||||||
$_ADDONLANG['Firewall']['TCP'] = 'TCP';
|
$_ADDONLANG['Firewall']['TCP'] = 'TCP';
|
||||||
$_ADDONLANG['Firewall']['UDP'] = 'UDP';
|
$_ADDONLANG['Firewall']['UDP'] = 'UDP';
|
||||||
$_ADDONLANG['Firewall']['ICMP'] = 'ICMP';
|
$_ADDONLANG['Firewall']['ICMP'] = 'ICMP';
|
||||||
|
$_ADDONLANG['Firewall']['Incoming'] = 'Входящий';
|
||||||
|
$_ADDONLANG['Firewall']['Outgoing'] = 'Исходящий';
|
||||||
|
$_ADDONLANG['Settings']['Firewall']['Direction'] = 'Направление';
|
||||||
|
|
||||||
## Reinstall specific
|
## Reinstall specific
|
||||||
$_ADDONLANG['Reinstall']['DestroyWarning'] = 'Перестроение уничтожит все данные на сервере. Новый root пароль будет сгенерирован и сохранен в вашей учетной записи службы.';
|
$_ADDONLANG['Reinstall']['DestroyWarning'] = 'Перестроение уничтожит все данные на сервере. Новый root пароль будет сгенерирован и сохранен в вашей учетной записи службы.';
|
||||||
|
|||||||
@@ -149,20 +149,11 @@ $_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Reinstalar';
|
|||||||
|
|
||||||
### Firewall
|
### Firewall
|
||||||
$_ADDONLANG['Settings']['Firewall']['Title'] = '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']['Action'] = 'Acción';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Puerto';
|
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Puerto';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocolo';
|
$_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']['Accept'] = 'Aceptar';
|
||||||
$_ADDONLANG['Settings']['Firewall']['Drop'] = 'Descartar';
|
$_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
|
### Rescue Mode
|
||||||
$_ADDONLANG['Settings']['Rescue']['Title'] = 'Modo de Rescate';
|
$_ADDONLANG['Settings']['Rescue']['Title'] = 'Modo de Rescate';
|
||||||
@@ -222,11 +213,16 @@ $_ADDONLANG['General']['Colon'] = ': ';
|
|||||||
## Firewall specific
|
## Firewall specific
|
||||||
$_ADDONLANG['Firewall']['CurrentRules'] = 'Reglas Actuales del Firewall';
|
$_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']['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']['Direction'] = 'Dirección';
|
||||||
|
$_ADDONLANG['Firewall']['Action'] = 'Acción';
|
||||||
|
$_ADDONLANG['Firewall']['Protocol'] = 'Protocolo';
|
||||||
|
$_ADDONLANG['Firewall']['Port'] = 'Puerto';
|
||||||
|
$_ADDONLANG['Firewall']['IPCIDR'] = 'IP/CIDR';
|
||||||
|
$_ADDONLANG['Firewall']['Remove'] = 'Eliminar';
|
||||||
$_ADDONLANG['Firewall']['ChangesImmediate'] = 'Los cambios del firewall se aplican inmediatamente. No es necesario confirmar cambios.';
|
$_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']['AddNewRule'] = 'Agregar Nueva Regla del Firewall';
|
||||||
$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535';
|
$_ADDONLANG['Firewall']['PortPlaceholder'] = '1-65535';
|
||||||
$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0';
|
$_ADDONLANG['Firewall']['SourcePlaceholder'] = '0.0.0.0/0';
|
||||||
$_ADDONLANG['Firewall']['DescriptionPlaceholder'] = 'Descripción de la regla';
|
|
||||||
$_ADDONLANG['Firewall']['Accept'] = 'ACEPTAR';
|
$_ADDONLANG['Firewall']['Accept'] = 'ACEPTAR';
|
||||||
$_ADDONLANG['Firewall']['Drop'] = 'DESCARTAR';
|
$_ADDONLANG['Firewall']['Drop'] = 'DESCARTAR';
|
||||||
$_ADDONLANG['Firewall']['Info'] = 'INFO';
|
$_ADDONLANG['Firewall']['Info'] = 'INFO';
|
||||||
@@ -234,6 +230,9 @@ $_ADDONLANG['Firewall']['Any'] = 'CUALQUIERA';
|
|||||||
$_ADDONLANG['Firewall']['TCP'] = 'TCP';
|
$_ADDONLANG['Firewall']['TCP'] = 'TCP';
|
||||||
$_ADDONLANG['Firewall']['UDP'] = 'UDP';
|
$_ADDONLANG['Firewall']['UDP'] = 'UDP';
|
||||||
$_ADDONLANG['Firewall']['ICMP'] = 'ICMP';
|
$_ADDONLANG['Firewall']['ICMP'] = 'ICMP';
|
||||||
|
$_ADDONLANG['Firewall']['Incoming'] = 'Entrante';
|
||||||
|
$_ADDONLANG['Firewall']['Outgoing'] = 'Saliente';
|
||||||
|
$_ADDONLANG['Settings']['Firewall']['Direction'] = 'Dirección';
|
||||||
|
|
||||||
## Reinstall specific
|
## 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.';
|
$_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.';
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
// Initialize template variables for external script
|
// Initialize template variables for external script
|
||||||
var productURL = '{$WEB_ROOT}/clientarea.php?action=productdetails&id={$serviceid}';
|
var productURL = '{$WEB_ROOT}/clientarea.php?action=productdetails&id={$serviceid}';
|
||||||
var serverInfoInitial = JSON.parse('{$serverInfo|@json_encode}');
|
var serverInfoInitial = JSON.parse('{$serverInfo|json_encode}');
|
||||||
var serviceId = '{$serviceid}';
|
var serviceId = '{$serviceid}';
|
||||||
var csrfToken = '{$token}';
|
var csrfToken = '{$token}';
|
||||||
var webRoot = '{$WEB_ROOT}';
|
var webRoot = '{$WEB_ROOT}';
|
||||||
@@ -211,7 +211,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-muted">{$ADDONLANG.Overview.ServerType}:</td>
|
<td class="text-muted">{$ADDONLANG.Overview.ServerType}:</td>
|
||||||
<td><strong>{$serverInfo['server_type']['description']|default:$ADDONLANG.ServerTypes.Standard}</strong></td>
|
<td><strong>{$productName}</strong></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -676,6 +676,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label class="small text-muted">{$ADDONLANG.Settings.Firewall.Direction}</label>
|
||||||
|
<select class="form-control" id="firewallDirection">
|
||||||
|
<option value="in" selected>{$ADDONLANG.Firewall.Incoming}</option>
|
||||||
|
<option value="out">{$ADDONLANG.Firewall.Outgoing}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
<label class="small text-muted">{$ADDONLANG.Settings.Firewall.Action}</label>
|
<label class="small text-muted">{$ADDONLANG.Settings.Firewall.Action}</label>
|
||||||
<select class="form-control" id="firewallAction">
|
<select class="form-control" id="firewallAction">
|
||||||
@@ -692,18 +699,14 @@
|
|||||||
<option value="ANY">{$ADDONLANG.Firewall.Any}</option>
|
<option value="ANY">{$ADDONLANG.Firewall.Any}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-2">
|
<div class="col-md-3">
|
||||||
<label class="small text-muted">{$ADDONLANG.Settings.Firewall.Port}</label>
|
<label class="small text-muted">{$ADDONLANG.Settings.Firewall.Port}</label>
|
||||||
<input class="form-control" id="firewallPort" type="number" min="1" max="65535" placeholder="{$ADDONLANG.Firewall.PortPlaceholder}">
|
<input class="form-control" id="firewallPort" type="text" placeholder="{$ADDONLANG.Firewall.PortPlaceholder}">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label class="small text-muted">{$ADDONLANG.Settings.Firewall.Source}</label>
|
<label class="small text-muted">IP/CIDR</label>
|
||||||
<input class="form-control" id="firewallSource" type="text" placeholder="{$ADDONLANG.Firewall.SourcePlaceholder}">
|
<input class="form-control" id="firewallSource" type="text" placeholder="{$ADDONLANG.Firewall.SourcePlaceholder}">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
|
||||||
<label class="small text-muted">{$ADDONLANG.Settings.Firewall.Note}</label>
|
|
||||||
<input class="form-control" id="firewallNote" type="text" maxlength="64" placeholder="{$ADDONLANG.Firewall.DescriptionPlaceholder}">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="text-right mt-3">
|
<div class="text-right mt-3">
|
||||||
<button class="btn btn-success" onclick="addFirewallRule();return false;">
|
<button class="btn btn-success" onclick="addFirewallRule();return false;">
|
||||||
@@ -723,12 +726,12 @@
|
|||||||
<table id="firewallTable" class="table table-hover mb-0">
|
<table id="firewallTable" class="table table-hover mb-0">
|
||||||
<thead class="thead-light">
|
<thead class="thead-light">
|
||||||
<tr>
|
<tr>
|
||||||
<th width="100">Action</th>
|
<th width="80">{$ADDONLANG.Firewall.Direction}</th>
|
||||||
<th width="100">Protocol</th>
|
<th width="100">{$ADDONLANG.Firewall.Action}</th>
|
||||||
<th width="100">Port</th>
|
<th width="100">{$ADDONLANG.Firewall.Protocol}</th>
|
||||||
<th>Source</th>
|
<th width="100">{$ADDONLANG.Firewall.Port}</th>
|
||||||
<th>Description</th>
|
<th>{$ADDONLANG.Firewall.IPCIDR}</th>
|
||||||
<th width="80" class="text-center">Remove</th>
|
<th width="80" class="text-center">{$ADDONLANG.Firewall.Remove}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -753,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>
|
||||||
|
|||||||
@@ -224,6 +224,12 @@ function resetRootPassword() {
|
|||||||
function() {
|
function() {
|
||||||
ArkHostHetznerVPS_API('Reset root', true, {}, function(data) {
|
ArkHostHetznerVPS_API('Reset root', true, {}, function(data) {
|
||||||
if (data.root_password) {
|
if (data.root_password) {
|
||||||
|
// Update the password field on the page
|
||||||
|
var passwordField = document.getElementById('vpsPassword');
|
||||||
|
if (passwordField) {
|
||||||
|
passwordField.value = data.root_password;
|
||||||
|
}
|
||||||
|
|
||||||
// Show password in a special modal or notification
|
// Show password in a special modal or notification
|
||||||
var passwordHtml = '<div class="alert alert-success">' +
|
var passwordHtml = '<div class="alert alert-success">' +
|
||||||
'<h5>' + lang.newRootPassword + '</h5>' +
|
'<h5>' + lang.newRootPassword + '</h5>' +
|
||||||
@@ -426,11 +432,11 @@ function ArkHostHetznerVPS_API(action, showAlert, params, callback, errorCallbac
|
|||||||
}
|
}
|
||||||
if (action === 'Add Firewall rules') {
|
if (action === 'Add Firewall rules') {
|
||||||
// Clear the form
|
// Clear the form
|
||||||
|
document.getElementById('firewallDirection').value = 'in';
|
||||||
document.getElementById('firewallAction').value = 'ACCEPT';
|
document.getElementById('firewallAction').value = 'ACCEPT';
|
||||||
document.getElementById('firewallPort').value = '';
|
document.getElementById('firewallPort').value = '';
|
||||||
document.getElementById('firewallProtocol').value = 'ANY';
|
document.getElementById('firewallProtocol').value = 'ANY';
|
||||||
document.getElementById('firewallSource').value = '';
|
document.getElementById('firewallSource').value = '';
|
||||||
document.getElementById('firewallNote').value = '';
|
|
||||||
|
|
||||||
// Refresh firewall rules after adding
|
// Refresh firewall rules after adding
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
@@ -515,8 +521,10 @@ function updateBackupTable(data) {
|
|||||||
backupDate = backupDate.split('T')[0];
|
backupDate = backupDate.split('T')[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine backup type
|
// Determine backup type based on description
|
||||||
var backupType = backup.type === 'backup' ? lang.backups.automatic : lang.backups.manual;
|
// Manual backups have descriptions starting with "Manual backup -"
|
||||||
|
var backupName = backup.name || '';
|
||||||
|
var backupType = backupName.indexOf('Manual backup -') === 0 ? lang.backups.manual : lang.backups.automatic;
|
||||||
|
|
||||||
// Format size (already in GB from API)
|
// Format size (already in GB from API)
|
||||||
var sizeStr = 'N/A';
|
var sizeStr = 'N/A';
|
||||||
@@ -524,22 +532,40 @@ function updateBackupTable(data) {
|
|||||||
sizeStr = backup.size + ' ' + lang.general.gb;
|
sizeStr = backup.size + ' ' + lang.general.gb;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status is always available for Hetzner backups
|
// Determine status badge based on size first (more reliable than API status)
|
||||||
var statusBadge = '<span class="badge badge-success">' + lang.backups.available + '</span>';
|
var statusBadge;
|
||||||
|
if (!backup.size || backup.size === 0 || backup.size === '0') {
|
||||||
|
// Backup is still being created (size not available yet)
|
||||||
|
statusBadge = '<span class="badge badge-warning">' + lang.backups.creating + '</span>';
|
||||||
|
} else if (backup.status === 'available' || backup.size > 0) {
|
||||||
|
// Backup is ready (has a size)
|
||||||
|
statusBadge = '<span class="badge badge-success">' + lang.backups.available + '</span>';
|
||||||
|
} else if (backup.status === 'creating') {
|
||||||
|
// Status says creating but has size (unlikely but possible)
|
||||||
|
statusBadge = '<span class="badge badge-warning">' + lang.backups.creating + '</span>';
|
||||||
|
} else {
|
||||||
|
// Other statuses (error, etc)
|
||||||
|
statusBadge = '<span class="badge badge-danger">' + (backup.status || lang.backups.error) + '</span>';
|
||||||
|
}
|
||||||
|
|
||||||
// Use backup ID for Hetzner
|
// Use backup ID for Hetzner
|
||||||
var backupId = backup.id || backup.file;
|
var backupId = backup.id || backup.file;
|
||||||
|
|
||||||
|
// Disable buttons if backup is still being created (size not available)
|
||||||
|
var isCreating = (!backup.size || backup.size === 0 || backup.size === '0');
|
||||||
|
var disabledAttr = isCreating ? ' disabled' : '';
|
||||||
|
var disabledClass = isCreating ? ' disabled' : '';
|
||||||
|
|
||||||
row.innerHTML =
|
row.innerHTML =
|
||||||
'<td>' + backupDate + '</td>' +
|
'<td>' + backupDate + '</td>' +
|
||||||
'<td>' + sizeStr + '</td>' +
|
'<td>' + sizeStr + '</td>' +
|
||||||
'<td>' + backupType + '</td>' +
|
'<td>' + backupType + '</td>' +
|
||||||
'<td>' + statusBadge + '</td>' +
|
'<td>' + statusBadge + '</td>' +
|
||||||
'<td style="white-space: nowrap;">' +
|
'<td style="white-space: nowrap;">' +
|
||||||
'<button class="btn btn-xs btn-primary mr-1" style="padding: 4px 8px; font-size: 12px;" onclick="restoreBackup(\'' + backupId + '\'); return false;" title="' + lang.restore + '">' +
|
'<button class="btn btn-xs btn-primary mr-1' + disabledClass + '" style="padding: 4px 8px; font-size: 12px;" onclick="restoreBackup(\'' + backupId + '\'); return false;" title="' + lang.restore + '"' + disabledAttr + '>' +
|
||||||
'<i class="fa fa-undo"></i>' +
|
'<i class="fa fa-undo"></i>' +
|
||||||
'</button>' +
|
'</button>' +
|
||||||
'<button class="btn btn-xs btn-danger" style="padding: 4px 8px; font-size: 12px;" onclick="deleteBackup(\'' + backupId + '\'); return false;" title="' + lang.delete + '">' +
|
'<button class="btn btn-xs btn-danger' + disabledClass + '" style="padding: 4px 8px; font-size: 12px;" onclick="deleteBackup(\'' + backupId + '\'); return false;" title="' + lang.delete + '"' + disabledAttr + '>' +
|
||||||
'<i class="fa fa-trash"></i>' +
|
'<i class="fa fa-trash"></i>' +
|
||||||
'</button>' +
|
'</button>' +
|
||||||
'</td>';
|
'</td>';
|
||||||
@@ -629,13 +655,21 @@ function updateFirewallTable(data) {
|
|||||||
|
|
||||||
var protocolBadge = '<span class="badge badge-secondary">' + (rule.protocol || lang.firewall.any) + '</span>';
|
var protocolBadge = '<span class="badge badge-secondary">' + (rule.protocol || lang.firewall.any) + '</span>';
|
||||||
|
|
||||||
|
// Direction badge
|
||||||
|
var directionBadge = '';
|
||||||
|
if (rule.direction === 'out') {
|
||||||
|
directionBadge = '<span class="badge badge-warning"><i class="fa fa-arrow-up mr-1"></i>' + (lang.firewall.outgoing || 'OUT') + '</span>';
|
||||||
|
} else {
|
||||||
|
directionBadge = '<span class="badge badge-info"><i class="fa fa-arrow-down mr-1"></i>' + (lang.firewall.incoming || 'IN') + '</span>';
|
||||||
|
}
|
||||||
|
|
||||||
var row = document.createElement('tr');
|
var row = document.createElement('tr');
|
||||||
row.innerHTML =
|
row.innerHTML =
|
||||||
|
'<td>' + directionBadge + '</td>' +
|
||||||
'<td>' + actionBadge + '</td>' +
|
'<td>' + actionBadge + '</td>' +
|
||||||
'<td>' + protocolBadge + '</td>' +
|
'<td>' + protocolBadge + '</td>' +
|
||||||
'<td>' + (rule.port || '<span class="text-muted">' + lang.general.any + '</span>') + '</td>' +
|
'<td>' + (rule.port || '<span class="text-muted">' + lang.general.any + '</span>') + '</td>' +
|
||||||
'<td><code>' + (rule.source || '0.0.0.0/0') + '</code></td>' +
|
'<td><code>' + (rule.source || '0.0.0.0/0') + '</code></td>' +
|
||||||
'<td>' + (rule.note || '<span class="text-muted">' + lang.general.emptyValue + '</span>') + '</td>' +
|
|
||||||
'<td class="text-center">' +
|
'<td class="text-center">' +
|
||||||
(rule.id !== 'info' ?
|
(rule.id !== 'info' ?
|
||||||
'<button class="btn btn-sm btn-danger" onclick="deleteFirewallRule(\'' + rule.id + '\'); return false;" title="' + lang.delete + '">' +
|
'<button class="btn btn-sm btn-danger" onclick="deleteFirewallRule(\'' + rule.id + '\'); return false;" title="' + lang.delete + '">' +
|
||||||
@@ -652,25 +686,58 @@ function updateFirewallTable(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addFirewallRule() {
|
function addFirewallRule() {
|
||||||
|
var direction = document.getElementById('firewallDirection').value;
|
||||||
var action = document.getElementById('firewallAction').value;
|
var action = document.getElementById('firewallAction').value;
|
||||||
var protocol = document.getElementById('firewallProtocol').value;
|
var protocol = document.getElementById('firewallProtocol').value;
|
||||||
var port = document.getElementById('firewallPort').value;
|
var port = document.getElementById('firewallPort').value;
|
||||||
var source = document.getElementById('firewallSource').value || '0.0.0.0/0';
|
var source = document.getElementById('firewallSource').value || '0.0.0.0/0';
|
||||||
var note = document.getElementById('firewallNote').value;
|
|
||||||
|
|
||||||
// Validate port for TCP/UDP
|
// Validate port for TCP/UDP
|
||||||
if ((protocol === 'TCP' || protocol === 'UDP') && !port) {
|
if ((protocol === 'TCP' || protocol === 'UDP' || protocol === 'ANY') && !port) {
|
||||||
showNotification(lang.messages.portRequired, 'error');
|
showNotification(lang.messages.portRequired, 'error');
|
||||||
document.getElementById('firewallPort').focus();
|
document.getElementById('firewallPort').focus();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate port format (single port or range)
|
||||||
|
if (port) {
|
||||||
|
// Check if it's a range (e.g., "8080-8090")
|
||||||
|
if (port.includes('-')) {
|
||||||
|
var parts = port.split('-');
|
||||||
|
if (parts.length !== 2) {
|
||||||
|
showNotification('Invalid port range format. Use format: 8080-8090', 'error');
|
||||||
|
document.getElementById('firewallPort').focus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var startPort = parseInt(parts[0]);
|
||||||
|
var endPort = parseInt(parts[1]);
|
||||||
|
if (isNaN(startPort) || isNaN(endPort) || startPort < 1 || startPort > 65535 || endPort < 1 || endPort > 65535) {
|
||||||
|
showNotification('Port range values must be between 1 and 65535', 'error');
|
||||||
|
document.getElementById('firewallPort').focus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (startPort >= endPort) {
|
||||||
|
showNotification('Start port must be less than end port', 'error');
|
||||||
|
document.getElementById('firewallPort').focus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Single port
|
||||||
|
var portNum = parseInt(port);
|
||||||
|
if (isNaN(portNum) || portNum < 1 || portNum > 65535) {
|
||||||
|
showNotification('Port must be between 1 and 65535', 'error');
|
||||||
|
document.getElementById('firewallPort').focus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ArkHostHetznerVPS_API('Add Firewall rules', true, {
|
ArkHostHetznerVPS_API('Add Firewall rules', true, {
|
||||||
|
direction: direction,
|
||||||
firewallAction: action,
|
firewallAction: action,
|
||||||
protocol: protocol,
|
protocol: protocol,
|
||||||
source: source,
|
source: source,
|
||||||
port: port,
|
port: port
|
||||||
note: note
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -68,6 +68,68 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
.arkhost-vps-container .btn-danger,
|
||||||
|
.arkhost-vps-container button.btn-danger,
|
||||||
|
.arkhost-vps-container .btn.btn-danger,
|
||||||
|
.arkhost-vps-container button.btn.btn-danger {
|
||||||
|
background: linear-gradient(135deg, #dc3545 0%, #c82333 100%) !important;
|
||||||
|
background-color: #dc3545 !important;
|
||||||
|
color: white !important;
|
||||||
|
border: none !important;
|
||||||
|
border-color: #dc3545 !important;
|
||||||
|
padding: 10px 20px !important;
|
||||||
|
border-radius: 12px !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
transition: all 0.3s ease !important;
|
||||||
|
box-shadow: 0 2px 8px rgba(220, 53, 69, 0.3) !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
.arkhost-vps-container .btn-danger:hover,
|
||||||
|
.arkhost-vps-container button.btn-danger:hover,
|
||||||
|
.arkhost-vps-container .btn.btn-danger:hover,
|
||||||
|
.arkhost-vps-container button.btn.btn-danger:hover,
|
||||||
|
.arkhost-vps-container .btn-danger:focus,
|
||||||
|
.arkhost-vps-container button.btn-danger:focus {
|
||||||
|
background: linear-gradient(135deg, #c82333 0%, #bd2130 100%) !important;
|
||||||
|
background-color: #c82333 !important;
|
||||||
|
color: white !important;
|
||||||
|
border-color: #c82333 !important;
|
||||||
|
box-shadow: 0 4px 12px rgba(220, 53, 69, 0.5) !important;
|
||||||
|
transform: translateY(-1px) !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
.arkhost-vps-container .btn-danger:active,
|
||||||
|
.arkhost-vps-container button.btn-danger:active {
|
||||||
|
background: #bd2130 !important;
|
||||||
|
background-color: #bd2130 !important;
|
||||||
|
border-color: #bd2130 !important;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extra specific selectors for theme compatibility (Lagom, etc) */
|
||||||
|
.arkhost-vps-container .card .btn-danger,
|
||||||
|
.arkhost-vps-container .card-body .btn-danger,
|
||||||
|
.arkhost-vps-container .tab-pane .btn-danger,
|
||||||
|
#ArkHostVPS .btn-danger,
|
||||||
|
#ArkHostVPS button.btn-danger {
|
||||||
|
background: linear-gradient(135deg, #dc3545 0%, #c82333 100%) !important;
|
||||||
|
background-color: #dc3545 !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
border-color: #dc3545 !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
.arkhost-vps-container .card .btn-danger:hover,
|
||||||
|
.arkhost-vps-container .card-body .btn-danger:hover,
|
||||||
|
.arkhost-vps-container .tab-pane .btn-danger:hover,
|
||||||
|
#ArkHostVPS .btn-danger:hover,
|
||||||
|
#ArkHostVPS button.btn-danger:hover {
|
||||||
|
background: linear-gradient(135deg, #c82333 0%, #bd2130 100%) !important;
|
||||||
|
background-color: #c82333 !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
border-color: #c82333 !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.arkhost-vps-container .head {
|
.arkhost-vps-container .head {
|
||||||
border-bottom: 2px solid #3c7fb4;
|
border-bottom: 2px solid #3c7fb4;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|||||||
Reference in New Issue
Block a user