18 Commits
Author SHA1 Message Date
Yuri Karamian 61b4c576c9 Bump version to 1.6 2026-06-22 01:31:44 +02:00
Yuri Karamian 7ccc92a039 Harden VPSAG module: credential logging, path injection, suspension, signature, XSS
- Redact decrypted reseller API credentials from module logs (ArkhostVPSAG_Error).
- Validate client-controlled values before they enter upstream API URL paths
  (graph time, backup file, firewall rule id, ISO id, rDNS IP); restrict VPS ID
  to digits. Prevents path/parameter injection (IDOR).
- Suspend via Disable/Enable instead of Stop/Start, and reject state-changing
  client-area actions on non-active services (suspended client can't power on).
- Verify the provisioning callback signature with hash_equals instead of !=.
- Fix callback assigned-IP assembly: keep all extra IPv4s, join with real
  newlines (was dropping the first IP and using a literal backslash-n).
- Make callback html_entity_decode array-safe.
- Escape untrusted output: JSON_HEX_TAG-encode serverInfo for the <script>
  block, HTML-escape serverInfo/OS fields in template + error.tpl, escape
  firewall/backup values built via innerHTML.
2026-06-22 00:53:52 +02:00
Yuri Karamian 00a1921c77 vnc embedded option 2025-12-18 23:57:38 +01:00
Yuri Karamian ac2c3f08d9 vnc embedded option 2025-12-18 23:52:35 +01:00
Yuri Karamian f2f2222df7 vnc embedded option 2025-12-18 23:41:18 +01:00
Yuri Karamian 9c09719fb9 update README 2025-12-16 22:26:23 +01:00
Yuri Karamian ee3da3f0e7 add update OS/Package IDs 2025-12-16 22:25:09 +01:00
Yuri Karamian 804d05a600 update README 2025-12-16 22:23:07 +01:00
Yuri Karamian 1196783c2d update README 2025-12-16 22:12:57 +01:00
Yuri Karamian 94bc3045b8 update README 2025-12-16 22:08:31 +01:00
Yuri b4529ee9b2 README.md update 2025-11-19 23:41:46 +01:00
Yuri fae7960bbd README.md update 2025-11-19 23:40:06 +01:00
Yuri 0ed2a0076c README.md update with screenshots 2025-11-13 21:32:20 +01:00
Yuri 3efb34a506 fix sidebar actions 2025-11-13 11:00:13 +01:00
Yuri dbb00718c0 v1.4 2025-11-13 10:59:35 +01:00
Yuri dc5e603870 v1.4-final 2025-11-13 01:32:44 +01:00
Yuri e1424d34ee v1.4 2025-11-13 01:00:43 +01:00
Yuri 6edebcd5b1 initial commit 2025-11-13 00:58:17 +01:00
23 changed files with 2743 additions and 296 deletions
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2025 ArkHost
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+208 -8
View File
@@ -1,26 +1,221 @@
# WHMCS VPS Management Module - ArkHost VPSAG
A WHMCS server module for VPS management with multi-language support.
A WHMCS server module for VPSAG/EVPS VPS management with multi-language support.
**Note:** VPSAG has rebranded to [EVPS.net](https://evps.net). The API remains unchanged at `vpsag.com`.
Originally based on [andretunes/whmcs-module-vpsag](https://github.com/andretunes/whmcs-module-vpsag). Current version has been substantially rewritten.
## Features
- VPS control (start/stop/restart)
- Backup management
- VPS provisioning and lifecycle management
- VPS control (start/stop/restart/shutdown)
- Backup management (create/restore/delete)
- Firewall configuration
- OS reinstallation with advanced options
- Resource monitoring
- OS reinstallation with SSH key injection and post-install scripts
- Resource monitoring with graphs
- VNC console access
- 8 language support
- Responsive design
- Hostname and rDNS management
- ISO mounting
- 8 language support (English, Dutch, French, German, Italian, Portuguese, Russian, Spanish)
- Responsive client area design
## Requirements
- WHMCS 7.0+
- WHMCS 8.9+
- PHP 7.4+
- VPSAG/EVPS reseller account with API access
## Installation
1. Upload the `ArkhostVPSAG` folder to `/modules/servers/` in your WHMCS installation
2. Navigate to **Setup > Products/Services > Servers** in WHMCS admin
3. Add a new server with:
- **Module:** ArkhostVPSAG
- **Hostname:** `www.vpsag.com`
- **Username:** Your VPSAG API username
- **Password:** Your VPSAG API key
4. Click **Test Connection** to verify credentials
## Product Configuration
### Creating a Product
1. Go to **Setup > Products/Services > Products/Services**
2. Create a new product or edit an existing one
3. Under **Module Settings** tab:
- **Module Name:** ArkhostVPSAG
- **Server Group:** Select your VPSAG server group
### Configurable Options
The module uses two primary configurable options that are dynamically populated from the VPSAG API:
| Option | Config Key | Description |
|--------|------------|-------------|
| **Plan ID** | `configoption1` | VPS plan - dropdown populated from `/api/v1/plans` showing plan names with prices |
| **OS ID** | `configoption2` | Operating system - dropdown populated from `/api/v1/os/plan/{plan_id}` based on selected plan |
| **VNC Display Mode** | `configoption3` | How VNC console is displayed: `newtab` (opens in new browser tab) or `embedded` (shows in a tab within the client area) |
#### Setting Up Configurable Options
You can override the default product options using WHMCS Configurable Options:
1. Go to **Setup > Products/Services > Configurable Options**
2. Create a new group and link it to your VPSAG product(s)
3. Add options with these exact names:
- `planid|Plan` - To allow customers to select different plans
- `osid|Operating System` - To allow customers to select their OS during order
**Option format:** `{api_id}|{display_name}`
The numeric ID before the pipe is the API ID from VPSAG/EVPS.
#### Finding the IDs
The module displays IDs directly in the dropdowns (format: `ID | Name`):
1. Go to **Setup > Products/Services > Products/Services**
2. Edit any product using the ArkhostVPSAG module
3. Go to the **Module Settings** tab
4. The **Plan** and **Operating System** dropdowns show: `{id} | {name}`
Use those IDs when creating Configurable Options.
**API terminology:**
- `planid` = Package/Plan ID from VPSAG API
- `osid` = Operating System ID from VPSAG API
**Note:** OS availability varies by plan - you must select a Plan first before the OS dropdown populates.
The module checks for configurable options first, then falls back to product-level settings.
### Callback URL
For automatic provisioning updates (IP, password delivery), configure your callback URL in VPSAG:
```
https://yourdomain.com/modules/servers/ArkhostVPSAG/callback.php
```
The callback handler:
- Validates requests using SHA256 signature verification
- Updates service with username, password, IPs (IPv4/IPv6)
- Sets VPS label to `WHMCS {service_id}` for easy identification
## Module Functions
### Client Area Actions
| Action | Description |
|--------|-------------|
| Start/Stop/Restart | Basic power controls |
| VNC Console | Browser-based console access |
| Reinstall OS | Fresh install with optional SSH key and post-install script |
| Reset Root Password | Generate new root password |
| Hostname/rDNS | Set hostname and reverse DNS |
| Graphs | View CPU, RAM, disk, network usage over time |
| Backups | Create, restore, and delete backups |
| Firewall | Add/remove rules, commit changes |
| ISO | Mount/unmount ISO images |
### Admin Actions
| Action | Description |
|--------|-------------|
| Start/Stop/Reboot | Power controls from admin panel |
| VNC Console | Direct VNC link |
| Suspend/Unsuspend | Service suspension management |
| Terminate | Cancel VPS at VPSAG |
| Change Package | Upgrade to higher plan |
### Sidebar Buttons
The module adds custom sidebar buttons to the client area:
- Start, Stop, Restart, VNC Console
## API Integration
Base URL: `https://www.vpsag.com/api/v1/`
Authentication headers:
- `X_API_USER`: API username
- `X_API_KEY`: API key
The module handles all API communication internally with:
- TLS 1.2 enforcement
- 15-second timeout
- Full request/response logging via WHMCS module log
## VPS ID Storage
The VPS ID is stored in WHMCS service properties with key `vpsag|VPSAG ID` in format `VPSAG-{numeric_id}`.
## Language Customization
Override translations by creating files in:
```
/modules/servers/ArkhostVPSAG/lang/overrides/{language}.php
```
## Screenshots
### Overview
![Main Dashboard](screenshots/vpsag1.png)
### Graphs
![Graphs](screenshots/vpsag2.png)
### Backups
![Backups](screenshots/vpsag3.png)
### Firewall
![Firewall](screenshots/vpsag4.png)
### Settings
![Settings](screenshots/vpsag5.png)
## Changelog
### Version 1.6
- Security hardening:
- Decrypted reseller API credentials are no longer written to the module log
- Client-supplied values used in API URL paths are now validated (prevents path/parameter injection and cross-tenant access)
- Suspension now uses Disable/Enable, and VPS control actions are blocked while a service is not active
- Provisioning callback signature is verified with a constant-time comparison
- Output escaping added for server/OS data in the client area, settings, and error templates
- Fixed callback IP assignment: all additional IPv4 addresses are kept and stored newline-separated
### Version 1.5
- WHMCS v9 compatibility: Updated Smarty template syntax for Smarty v4
- Added VNC Display Mode setting: choose between new tab or embedded in client area
- Embedded VNC tab with fullscreen toggle and open in new tab buttons
- Plan and OS IDs now displayed in Module Settings dropdowns for easy configuration
- Updated VPSAG branding to EVPS.net (API unchanged)
- Improved documentation with configurable options setup guide
- Fixed session issues with VNC console
- Added iframe sandbox for security
### Version 1.4
- Fixed firewall rule duplication bug when adding new rules
- Added validation warning for ANY protocol with specific port numbers
- Redesigned firewall interface with modern card-based layout
- Created standalone Firewall tab for better organization
- Completely redesigned VPS Overview section with:
- Server Status Card (hostname, IPs, OS, status, uptime, server type)
- Resource Allocation Card (CPU cores, Memory, Storage, Traffic)
- Resource Usage Card (RAM, Bandwidth, CPU)
- Quick Actions Card (Start/Stop, Restart, VNC Console)
- Fixed Quick Actions button functionality
- Completed all language translations (8 languages)
- Enhanced UI styling with gradient backgrounds and smooth animations
- Improved responsive design and layout consistency
### Version 1.3
- Enhanced API integration
- Performance improvements
- Bug fixes
### Version 1.2
- Custom notifications replace browser popups
- Confirmation dialogs for critical actions
@@ -38,6 +233,11 @@ A WHMCS server module for VPS management with multi-language support.
### Version 1.0
- Initial release
## Credits
Originally based on work by [andretunes](https://github.com/andretunes/whmcs-module-vpsag)
Substantially rewritten and extended by ArkHost
## License
MIT License
© 2025 ArkHost
+197 -19
View File
@@ -3,14 +3,15 @@
* WHMCS Server Module - VPSAG
*
* @package WHMCS
* @version 1.2
* @version 1.6
* @copyright Copyright (c) ArkHost 2025
* @author ArkHost <support@arkhost.com>
* @link https://arkhost.com
*/
if (!defined('WHMCS')) {
exit(header('Location: https://arkhost.com'));
http_response_code(403);
exit('Access denied');
}
use WHMCS\Config\Setting;
@@ -18,8 +19,24 @@ use WHMCS\Database\Capsule;
function ArkhostVPSAG_GetVPSID(array $params) {
$vpsId = $params['model']->serviceProperties->get('vpsag');
// Handle both VPSAG-xxx and VPS-xxx formats, return just the numeric ID
return str_replace(['VPSAG-', 'VPS-'], '', $vpsId);
// Handle both VPSAG-xxx and VPS-xxx formats, return just the numeric ID.
// Strip everything except digits so the value is always safe in an API path.
return preg_replace('/[^0-9]/', '', (string) $vpsId);
}
/**
* Validate a client-supplied value before it is concatenated into an upstream
* API URL path. Rejects path traversal and any character outside the allowed
* set so a client cannot reshape the request (path/parameter injection / IDOR).
*/
function ArkhostVPSAG_SafePathSegment($value, $pattern, $label) {
$value = (string) $value;
if ($value === '' || strpos($value, '..') !== false || !preg_match($pattern, $value)) {
throw new Exception('Invalid ' . $label);
}
return $value;
}
function ArkhostVPSAG_API(array $params) {
@@ -54,7 +71,8 @@ function ArkhostVPSAG_API(array $params) {
break;
case 'Order':
$url .= 'order/plan';
$planId = ArkhostVPSAG_GetOption($params, 'planid');
$url .= 'order/' . $planId;
$method .= 'POST';
$billingCycles = array(
@@ -67,9 +85,9 @@ function ArkhostVPSAG_API(array $params) {
);
$data += array(
'plan_id' => ArkhostVPSAG_GetOption($params, 'planid'),
// 'hostname' => removed - let VPSAG generate the hostname automatically
'notify_url' => Setting::getValue('SystemURL') . '/modules/servers/ArkhostVPSAG/callback.php',
'os_id' => ArkhostVPSAG_GetOption($params, 'osid'),
'os' => ArkhostVPSAG_GetOption($params, 'osid'),
'billing_term' => $billingCycles[$params['model']['billingcycle']] ?? 1,
);
break;
@@ -89,7 +107,8 @@ function ArkhostVPSAG_API(array $params) {
break;
case 'Graphs':
$url .= 'vps/' . ArkhostVPSAG_GetVPSID($params) . '/graph/' . $params['time'];
$time = ArkhostVPSAG_SafePathSegment($params['time'], '/^[A-Za-z0-9_-]+$/', 'time period');
$url .= 'vps/' . ArkhostVPSAG_GetVPSID($params) . '/graph/' . $time;
$method .= 'GET';
break;
@@ -176,6 +195,9 @@ function ArkhostVPSAG_API(array $params) {
break;
case 'Reverse DNS':
if (!filter_var($params['ip'] ?? '', FILTER_VALIDATE_IP)) {
throw new Exception('Invalid IP address');
}
$url .= 'vps/' . ArkhostVPSAG_GetVPSID($params) . '/rdns/' . $params['ip'];
$method .= 'POST';
@@ -213,7 +235,8 @@ function ArkhostVPSAG_API(array $params) {
break;
case 'Delete backup':
$url .= 'vps/' . ArkhostVPSAG_GetVPSID($params) . '/backup/' . $params['file'];
$file = ArkhostVPSAG_SafePathSegment($params['file'], '/^[A-Za-z0-9._-]+$/', 'backup file');
$url .= 'vps/' . ArkhostVPSAG_GetVPSID($params) . '/backup/' . $file;
$method .= 'DELETE';
break;
@@ -223,7 +246,8 @@ function ArkhostVPSAG_API(array $params) {
break;
case 'Restore backup':
$url .= 'vps/' . ArkhostVPSAG_GetVPSID($params) . '/restore/' . $params['file'];
$file = ArkhostVPSAG_SafePathSegment($params['file'], '/^[A-Za-z0-9._-]+$/', 'backup file');
$url .= 'vps/' . ArkhostVPSAG_GetVPSID($params) . '/restore/' . $file;
$method .= 'POST';
break;
@@ -246,7 +270,8 @@ function ArkhostVPSAG_API(array $params) {
break;
case 'Delete Firewall rule':
$url .= 'vps/' . ArkhostVPSAG_GetVPSID($params) . '/firewall/' . $params['rule_id'];
$ruleId = ArkhostVPSAG_SafePathSegment($params['rule_id'], '/^[0-9]+$/', 'rule id');
$url .= 'vps/' . ArkhostVPSAG_GetVPSID($params) . '/firewall/' . $ruleId;
$method .= 'DELETE';
break;
@@ -261,7 +286,8 @@ function ArkhostVPSAG_API(array $params) {
break;
case 'Load ISO':
$url .= 'vps/' . ArkhostVPSAG_GetVPSID($params) . '/iso/' . $params['iso_id'];
$isoId = ArkhostVPSAG_SafePathSegment($params['iso_id'], '/^[0-9]+$/', 'ISO id');
$url .= 'vps/' . ArkhostVPSAG_GetVPSID($params) . '/iso/' . $isoId;
$method .= 'POST';
break;
@@ -317,10 +343,106 @@ function ArkhostVPSAG_API(array $params) {
}
function ArkhostVPSAG_Error($func, $params, Exception $err) {
logModuleCall('ArkHost - VPSAG', $func, $params, $err->getMessage(), $err->getTraceAsString());
// Never write the decrypted reseller API credentials into the module log.
$replaceVars = array();
if (is_array($params)) {
foreach (array('serverpassword', 'serverusername', 'password') as $sensitive) {
if (!empty($params[$sensitive]) && is_string($params[$sensitive])) {
$replaceVars[] = $params[$sensitive];
}
}
}
logModuleCall('ArkHost - VPSAG', $func, $params, $err->getMessage(), $err->getTraceAsString(), $replaceVars);
}
function ArkhostVPSAG_MetaData() {
// Register sidebar hook here to ensure it's always loaded
static $hookRegistered = false;
if (!$hookRegistered) {
$hookRegistered = true;
add_hook('ClientAreaPrimarySidebar', 100, function(\WHMCS\View\Menu\Item $primarySidebar) {
$serviceId = isset($_GET['id']) ? (int) $_GET['id'] : 0;
if (!$serviceId) return;
try {
$service = Capsule::table('tblhosting')
->join('tblproducts', 'tblhosting.packageid', '=', 'tblproducts.id')
->where('tblhosting.id', $serviceId)
->select('tblhosting.*', 'tblproducts.servertype')
->first();
if (!$service || $service->servertype !== 'ArkhostVPSAG' || $service->domainstatus !== 'Active') {
return;
}
} catch (Exception $e) {
return;
}
$actionsPanel = $primarySidebar->getChild('Service Details Actions');
if (!$actionsPanel) return;
// Load language strings
if (!function_exists('ArkhostVPSAG_Translation')) {
require_once __DIR__ . '/ArkhostVPSAG.php';
}
$_LANG = ArkhostVPSAG_Translation();
// Remove only OUR custom action buttons (created by ClientAreaCustomButtonArray with cog icons)
// They have modop=custom in the URI
$existingChildren = $actionsPanel->getChildren();
foreach ($existingChildren as $childName => $child) {
$uri = $child->getUri();
if ($uri && strpos($uri, 'modop=custom') !== false) {
$actionsPanel->removeChild($childName);
}
}
// Add custom action buttons with proper icons
$actionsPanel->addChild('VPSStart', [
'label' => $_LANG['Start'],
'uri' => 'clientarea.php?action=productdetails&id=' . $serviceId . '&modop=custom&a=Start',
'order' => 10,
'icon' => 'fa-play'
]);
$actionsPanel->addChild('VPSStop', [
'label' => $_LANG['Stop'],
'uri' => 'clientarea.php?action=productdetails&id=' . $serviceId . '&modop=custom&a=Stop',
'order' => 20,
'icon' => 'fa-stop'
]);
$actionsPanel->addChild('VPSRestart', [
'label' => $_LANG['Restart'],
'uri' => 'clientarea.php?action=productdetails&id=' . $serviceId . '&modop=custom&a=Reboot',
'order' => 30,
'icon' => 'fa-sync'
]);
// Check VNC mode from product config
$product = Capsule::table('tblproducts')->where('id', $service->packageid)->first();
$vncMode = isset($product->configoption3) && $product->configoption3 !== '' ? $product->configoption3 : 'newtab';
if ($vncMode === 'embedded') {
// For embedded mode, link to the page with hash to trigger VNC tab
$actionsPanel->addChild('VPSVNC', [
'label' => $_LANG['VNC'],
'uri' => 'clientarea.php?action=productdetails&id=' . $serviceId . '#vnc',
'order' => 40,
'icon' => 'fa-desktop'
]);
} else {
// For new tab mode, open VNC in new window
$actionsPanel->addChild('VPSVNC', [
'label' => $_LANG['VNC'],
'uri' => 'clientarea.php?action=productdetails&id=' . $serviceId . '&modop=custom&a=VNC',
'order' => 40,
'icon' => 'fa-desktop',
'attributes' => ['target' => '_blank']
]);
}
});
}
return array(
'DisplayName' => 'ArkHost - VPSAG',
'APIVersion' => '1.1',
@@ -350,6 +472,16 @@ function ArkhostVPSAG_ConfigOptions() {
'Type' => 'dropdown',
'Options' => array(),
),
'vnc_mode' => array(
'FriendlyName' => 'VNC Display Mode',
'Description' => 'How to display the VNC console to clients.',
'Type' => 'dropdown',
'Options' => array(
'newtab' => 'New Tab',
'embedded' => 'Embedded in Page',
),
'Default' => 'newtab',
),
// Extra resource options removed - now handled via plan selection
// Plans have fixed resources defined within them in the new API
);
@@ -399,7 +531,7 @@ function ArkhostVPSAG_ConfigOptions() {
foreach ($packageslist as $package) {
$array['planid']['Options'] += array(
$package['id'] => $package['name'] . ' (' . $package['price'] . '€)'
$package['id'] => $package['id'] . ' | ' . $package['name'] . ' (' . $package['price'] . '€)'
);
}
@@ -411,7 +543,7 @@ function ArkhostVPSAG_ConfigOptions() {
foreach ($operatingSystems as $operatingSystem) {
$array['osid']['Options'] += array(
$operatingSystem['id'] => $operatingSystem['name']
$operatingSystem['id'] => $operatingSystem['id'] . ' | ' . $operatingSystem['name']
);
}
@@ -483,6 +615,22 @@ function ArkhostVPSAG_CreateAccount(array $params) {
$params['action'] = 'Order';
$create = ArkhostVPSAG_API($params);
// Validate API response - the API function already handles status=0 errors
// but we need to ensure we have a valid vps_id
if (!is_array($create) || !isset($create['vps_id']) || empty($create['vps_id'])) {
if (is_array($create)) {
logActivity('VPSAG Order Failed - Invalid Response: ' . json_encode($create));
} else {
logActivity('VPSAG Order Failed - Non-array response: ' . gettype($create));
}
throw new Exception('Invalid response from API');
}
// Ensure vps_id is numeric and valid
if (!is_numeric($create['vps_id']) || $create['vps_id'] <= 0) {
throw new Exception('Invalid response from API');
}
$params['model']->serviceProperties->save([
'vpsag|VPSAG ID' => 'VPSAG-' . $create['vps_id'],
]);
@@ -496,6 +644,8 @@ function ArkhostVPSAG_CreateAccount(array $params) {
function ArkhostVPSAG_SuspendAccount(array $params) {
try {
// Use Disable (not Stop) so the client cannot simply power the VPS back
// on from the client area and defeat the suspension.
$params['action'] = 'Disable';
ArkhostVPSAG_API($params);
} catch (Exception $err) {
@@ -508,6 +658,7 @@ function ArkhostVPSAG_SuspendAccount(array $params) {
function ArkhostVPSAG_UnsuspendAccount(array $params) {
try {
// Mirror of SuspendAccount: re-enable the VPS that Disable suspended.
$params['action'] = 'Enable';
ArkhostVPSAG_API($params);
} catch (Exception $err) {
@@ -630,10 +781,18 @@ function ArkhostVPSAG_AdminLink(array $params) {
function ArkhostVPSAG_ClientAreaAPI(array $params) {
try {
$action = App::getFromRequest('api');
$actions = array('Server Info', 'Graphs', 'Reinstall', 'Reboot', 'Stop', 'Start', 'IPv4 Addresses', 'Hostname rDNS', 'Create backup', 'Delete backup', 'List backups', 'Restore backup', 'Get Firewall rules', 'Add Firewall rules', 'Delete Firewall rule', 'Commit Firewall rules', 'ISO Images', 'Load ISO', 'Eject ISO', 'Reset root');
$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', 'VNC Console');
$results = array('result' => 'success');
if (in_array($action, $actions)) {
// Block state-changing actions when the service is not Active, so a
// suspended or terminated client cannot control the VPS.
$stateChanging = array('Reinstall', 'Reboot', 'Stop', 'Start', 'Hostname rDNS', 'Create backup', 'Delete backup', 'Restore backup', 'Add Firewall rules', 'Delete Firewall rule', 'Commit Firewall rules', 'Load ISO', 'Eject ISO', 'Reset root');
$status = isset($params['model']) ? $params['model']->domainstatus : '';
if (in_array($action, $stateChanging) && $status !== 'Active') {
throw new Exception('This action is not available while the service is not active.');
}
foreach ($_POST as $key => $value) {
$params[$key] = $value;
}
@@ -719,6 +878,8 @@ function ArkhostVPSAG_DeliverFile(array $params) {
// This function is required for WHMCS to handle the custom actions
// The hook in MetaData() replaces the sidebar buttons to remove cog icons
function ArkhostVPSAG_ClientAreaCustomButtonArray() {
$_LANG = ArkhostVPSAG_Translation();
@@ -741,13 +902,13 @@ function ArkhostVPSAG_ClientArea(array $params) {
// Check if VPS ID is available
$vpsId = $params['model']->serviceProperties->get('vpsag');
if (empty($vpsId)) {
throw new Exception('VPS ID not found in service properties. This service may not be properly provisioned yet.');
throw new Exception('Service not properly provisioned');
}
// Get clean VPS ID
$cleanVpsId = ArkhostVPSAG_GetVPSID($params);
if (empty($cleanVpsId)) {
throw new Exception('Invalid VPS ID format: ' . $vpsId . '. Expected format: VPS-xxxxx or VPSAG-xxxxx');
throw new Exception('Invalid VPS ID format');
}
// Get server info and operating systems data
@@ -756,7 +917,14 @@ function ArkhostVPSAG_ClientArea(array $params) {
// Check if server info is valid
if (!is_array($serverInfo) || empty($serverInfo)) {
throw new Exception('Unable to retrieve server information from API. VPS ID: ' . $vpsId);
throw new Exception('Unable to retrieve server information from API');
}
// Sync the domain field with VPS hostname so it shows in WHMCS product details
if (isset($serverInfo['hostname']) && !empty($serverInfo['hostname'])) {
Capsule::table('tblhosting')
->where('id', $params['serviceid'])
->update(['domain' => $serverInfo['hostname']]);
}
$params['action'] = 'Operating Systems - Server';
@@ -890,6 +1058,7 @@ function ArkhostVPSAG_ClientArea(array $params) {
$serverInfo['hostname'] = isset($serverInfo['hostname']) ? $serverInfo['hostname'] : 'N/A';
$serverInfo['ip'] = isset($serverInfo['ip']) ? $serverInfo['ip'] : 'N/A';
$serverInfo['uptime_text'] = isset($serverInfo['uptime_text']) ? $serverInfo['uptime_text'] : 'N/A';
$serverInfo['server_type'] = isset($serverInfo['category']) ? $serverInfo['category'] : 'Standard';
$serverInfo['cpu_usage'] = isset($serverInfo['cpu_usage']) ? $serverInfo['cpu_usage'] : 0;
$serverInfo['ram_usage'] = isset($serverInfo['ram_usage']) ? $serverInfo['ram_usage'] : 0;
$serverInfo['ram'] = isset($serverInfo['ram']) ? $serverInfo['ram'] : 1;
@@ -902,14 +1071,23 @@ function ArkhostVPSAG_ClientArea(array $params) {
$serverInfo['bandwidth_used'] = isset($serverInfo['bandwidth_used']) ? $serverInfo['bandwidth_used'] : 0;
$serverInfo['bandwidth'] = isset($serverInfo['bandwidth']) ? $serverInfo['bandwidth'] : 1;
// Get VNC mode setting (configoption3)
$vncMode = isset($params['configoption3']) && $params['configoption3'] !== ''
? $params['configoption3']
: 'newtab';
return array(
'templatefile' => 'template/clientarea_direct',
'templateVariables' => array(
'images' => $images,
'serverInfo' => $serverInfo,
// Pre-encoded for safe embedding in a <script> block: JSON_HEX_TAG
// prevents a customer-set hostname from breaking out with </script>.
'serverInfoJson' => json_encode($serverInfo, JSON_HEX_TAG | JSON_HEX_AMP) ?: '{}',
'operatingSystems' => $operatingSystems,
'token' => generate_token('plain'),
'_LANG' => ArkhostVPSAG_Translation(),
'vncMode' => $vncMode,
)
);
} catch (Exception $err) {
+40 -9
View File
@@ -1,6 +1,6 @@
<?php
/**
* VPSAG WHMCS Server Provisioning version 1.2
* VPSAG WHMCS Server Provisioning version 1.6
*
* @package WHMCS
* @copyright ArkHost
@@ -9,7 +9,8 @@
*/
if (empty($_POST)) {
exit(header('Location: https://arkhost.com'));
http_response_code(400);
exit('Bad Request');
}
require_once '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'init.php';
@@ -17,12 +18,33 @@ require_once ROOTDIR . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . '
use WHMCS\Database\Capsule;
$_POST = array_map('html_entity_decode', $_POST);
$_POST = array_map(function ($value) {
return is_string($value) ? html_entity_decode($value) : $value;
}, $_POST);
// Look for the service by VPSAG ID in service properties
$serviceProperty = Capsule::table('tblservice_properties')
->where('property', 'vpsag|VPSAG ID')
->where('value', 'VPSAG-' . $_POST['server_id'])
->first();
if (!$serviceProperty) {
// Fallback: try to find in custom fields for backward compatibility
$customField = Capsule::table('tblcustomfieldsvalues')->where('value', 'VPSAG-' . $_POST['server_id'])->first();
if ($customField) {
$service = WHMCS\Service\Service::find($customField->relid);
} else {
http_response_code(404);
exit('Service not found');
}
} else {
$service = WHMCS\Service\Service::find($serviceProperty->service_id);
}
if (!$service) exit(header('Location: https://arkhost.com'));
if (!$service) {
http_response_code(404);
exit('Service not found');
}
$server = Capsule::table('tblservers')->where('id', $service->server)->first();
@@ -37,7 +59,10 @@ foreach ($_POST as $key => $value) {
$rawSig .= hash('sha512', decrypt($server->password));
$signature = hash('sha256', $rawSig);
if ($_POST['sig'] != $signature) exit(header('Location: https://arkhost.com'));
if (!hash_equals($signature, (string) ($_POST['sig'] ?? ''))) {
http_response_code(403);
exit('Invalid signature');
}
$params = array(
'serverusername' => $server->username,
@@ -56,15 +81,21 @@ $_POST['ips'] = array_map(function ($ip) {
$mainIP = $_POST['mainip'];
$_POST['ips'] = array_filter($_POST['ips'], function ($ip) use ($mainIP) {
return $ip !== $mainIP;
});
// Keep every additional IPv4 (exclude the primary IP and blanks), then append
// the IPv6 address. Stored newline-separated, as WHMCS expects.
$extraIps = array_values(array_filter($_POST['ips'], function ($ip) use ($mainIP) {
return $ip !== $mainIP && $ip !== '';
}));
if (!empty($_POST['ipv6'])) {
$extraIps[] = $_POST['ipv6'];
}
Capsule::table('tblhosting')->where('id', $service->id)->update(array(
'username' => $_POST['username'],
'password' => encrypt($_POST['root']),
'dedicatedip' => $mainIP,
'assignedips' => (!array_shift($_POST['ips']) ? $_POST['ipv6'] : implode('\n', $_POST['ips']) . '\n' . $_POST['ipv6']),
'assignedips' => implode("\n", $extraIps),
));
echo '*ok*';
+73
View File
@@ -0,0 +1,73 @@
<?php
/**
* WHMCS Server Module - VPSAG Hooks
*
* @package WHMCS
* @version 1.6
* @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 ArkhostVPSAG 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 ArkhostVPSAG module
try {
$service = Capsule::table('tblhosting')
->where('id', $serviceId)
->first();
if (!$service || $service->servertype !== 'ArkhostVPSAG') {
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 ArkhostVPSAG */
#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;
});
+30 -1
View File
@@ -10,7 +10,9 @@ $_ADDONLANG['Used'] = 'Gebruikt';
$_ADDONLANG['Start'] = 'Start';
$_ADDONLANG['Stop'] = 'Stop';
$_ADDONLANG['Restart'] = 'Herstarten';
$_ADDONLANG['VNC'] = 'VNC-console';
$_ADDONLANG['VNC'] = 'VNC';
$_ADDONLANG['General']['Note'] = 'Opmerking';
$_ADDONLANG['LoadingFirewallRules'] = 'Firewall-regels laden...';
## Bevestigingen
$_ADDONLANG['Confirm']['Stop']['Title'] = 'VPS stoppen';
@@ -33,11 +35,28 @@ $_ADDONLANG['Navbar']['Backups'] = 'Back-ups';
$_ADDONLANG['Navbar']['Settings'] = 'Instellingen';
## Overzicht
$_ADDONLANG['Overview']['ServerInfo'] = 'Server Informatie';
$_ADDONLANG['Overview']['Hostname'] = 'Hostnaam';
$_ADDONLANG['Overview']['Status'] = 'Status';
$_ADDONLANG['Overview']['Uptime'] = 'Uptime';
$_ADDONLANG['Overview']['OS'] = 'Besturingssysteem';
$_ADDONLANG['Overview']['Location'] = 'Locatie';
$_ADDONLANG['Overview']['ServerType'] = 'Servertype';
$_ADDONLANG['Overview']['ResourceAllocation'] = 'Toegewezen Bronnen';
$_ADDONLANG['Overview']['CPU_Cores'] = 'CPU-kernen';
$_ADDONLANG['Overview']['Memory'] = 'Geheugen';
$_ADDONLANG['Overview']['Storage'] = 'Opslag';
$_ADDONLANG['Overview']['Traffic'] = 'Verkeer';
$_ADDONLANG['Overview']['QuickActions'] = 'Snelle Acties';
$_ADDONLANG['Overview']['ResourceUsage'] = 'Gebruik van Bronnen';
$_ADDONLANG['Overview']['CPU'] = 'CPU-gebruik';
$_ADDONLANG['Overview']['CPUUsage'] = 'Gebruik';
$_ADDONLANG['Overview']['RAM'] = 'RAM-gebruik';
$_ADDONLANG['Overview']['Bandwidth'] = 'Bandbreedtegebruik';
$_ADDONLANG['Overview']['Disk'] = 'Schijfruimte';
$_ADDONLANG['Overview']['DiskSize'] = 'Totale grootte';
$_ADDONLANG['General']['GB'] = 'GB';
$_ADDONLANG['General']['TB'] = 'TB';
## Grafieken
$_ADDONLANG['Graphs']['CPU'] = 'CPU-gebruik';
@@ -49,6 +68,7 @@ $_ADDONLANG['Graphs']['Day'] = 'Dag';
$_ADDONLANG['Graphs']['Week'] = 'Week';
$_ADDONLANG['Graphs']['Month'] = 'Maand';
$_ADDONLANG['Graphs']['Year'] = 'Jaar';
$_ADDONLANG['Graphs']['Loading'] = 'Grafiek laden...';
## Back-ups
$_ADDONLANG['Backups']['Description'] = 'De datums waarop back-ups van deze VPS beschikbaar zijn, staan hieronder vermeld. U kunt ze dienovereenkomstig herstellen of verwijderen.';
@@ -89,10 +109,19 @@ $_ADDONLANG['Settings']['Reinstall']['Description'] = 'Begrijp alstublieft dat b
$_ADDONLANG['Settings']['Reinstall']['OS'] = 'Besturingssysteem';
$_ADDONLANG['Settings']['Reinstall']['Version'] = 'KIES VERSIE';
$_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Herinstalleren';
$_ADDONLANG['Settings']['Reinstall']['SSHKey'] = 'SSH Publieke Sleutel (optioneel)';
$_ADDONLANG['Settings']['Reinstall']['SSHKeyDesc'] = 'Plak hier uw openbare SSH-sleutel (begint met ssh-rsa, ssh-ed25519, etc.). Deze wordt automatisch toegevoegd aan ~/.ssh/authorized_keys voor wachtwoordloos inloggen.';
$_ADDONLANG['Settings']['Reinstall']['PostScript'] = 'Post-installatiescript (optioneel)';
$_ADDONLANG['Settings']['Reinstall']['PostScriptDesc'] = 'Voer een bash-script in dat automatisch wordt uitgevoerd nadat de OS-installatie is voltooid. Handig voor het installeren van pakketten, het configureren van services of het instellen van uw omgeving.';
$_ADDONLANG['Settings']['Reinstall']['DisableSSHPassword'] = 'SSH-wachtwoordauthenticatie uitschakelen';
$_ADDONLANG['Settings']['Reinstall']['DisableSSHPasswordDesc'] = 'Indien ingeschakeld, is alleen SSH-sleutelauthenticatie toegestaan. Inloggen met wachtwoord via SSH wordt uitgeschakeld voor verbeterde beveiliging.';
### 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 openbare interface. Alleen het inkomende verkeer wordt door de firewall gefilterd.';
$_ADDONLANG['Settings']['Firewall']['AddNewRule'] = 'Nieuwe Firewallregel Toevoegen';
$_ADDONLANG['Settings']['Firewall']['CurrentRules'] = 'Huidige Firewallregels';
$_ADDONLANG['Settings']['Firewall']['AddRule'] = 'Regel Toevoegen';
$_ADDONLANG['Settings']['Firewall']['Action'] = 'Actie';
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Poort';
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocol';
+23 -1
View File
@@ -10,7 +10,9 @@ $_ADDONLANG['Used'] = 'Used';
$_ADDONLANG['Start'] = 'Start';
$_ADDONLANG['Stop'] = 'Stop';
$_ADDONLANG['Restart'] = 'Restart';
$_ADDONLANG['VNC'] = 'VNC Console';
$_ADDONLANG['VNC'] = 'VNC';
$_ADDONLANG['General']['Note'] = 'Note';
$_ADDONLANG['LoadingFirewallRules'] = 'Loading firewall rules...';
## Confirmations
$_ADDONLANG['Confirm']['Stop']['Title'] = 'Stop VPS';
@@ -33,11 +35,28 @@ $_ADDONLANG['Navbar']['Backups'] = 'Backups';
$_ADDONLANG['Navbar']['Settings'] = 'Settings';
## Overview
$_ADDONLANG['Overview']['ServerInfo'] = 'Server Information';
$_ADDONLANG['Overview']['Hostname'] = 'Hostname';
$_ADDONLANG['Overview']['Status'] = 'Status';
$_ADDONLANG['Overview']['Uptime'] = 'Uptime';
$_ADDONLANG['Overview']['OS'] = 'Operating System';
$_ADDONLANG['Overview']['Location'] = 'Location';
$_ADDONLANG['Overview']['ServerType'] = 'Server Type';
$_ADDONLANG['Overview']['ResourceAllocation'] = 'Resource Allocation';
$_ADDONLANG['Overview']['CPU_Cores'] = 'CPU Cores';
$_ADDONLANG['Overview']['Memory'] = 'Memory';
$_ADDONLANG['Overview']['Storage'] = 'Storage';
$_ADDONLANG['Overview']['Traffic'] = 'Traffic';
$_ADDONLANG['Overview']['QuickActions'] = 'Quick Actions';
$_ADDONLANG['Overview']['ResourceUsage'] = 'Resource Usage';
$_ADDONLANG['Overview']['CPU'] = 'CPU Usage';
$_ADDONLANG['Overview']['CPUUsage'] = 'Usage';
$_ADDONLANG['Overview']['RAM'] = 'RAM Usage';
$_ADDONLANG['Overview']['Bandwidth'] = 'Bandwidth Usage';
$_ADDONLANG['Overview']['Disk'] = 'Disk Space';
$_ADDONLANG['Overview']['DiskSize'] = 'Total Size';
$_ADDONLANG['General']['GB'] = 'GB';
$_ADDONLANG['General']['TB'] = 'TB';
## Graphs
$_ADDONLANG['Graphs']['CPU'] = 'CPU Usage';
@@ -100,6 +119,9 @@ $_ADDONLANG['Settings']['Reinstall']['DisableSSHPasswordDesc'] = 'When enabled,
### 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']['AddNewRule'] = 'Add New Firewall Rule';
$_ADDONLANG['Settings']['Firewall']['CurrentRules'] = 'Current Firewall Rules';
$_ADDONLANG['Settings']['Firewall']['AddRule'] = 'Add Rule';
$_ADDONLANG['Settings']['Firewall']['Action'] = 'Action';
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Port';
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocol';
+23 -2
View File
@@ -10,7 +10,9 @@ $_ADDONLANG['Used'] = 'Utilisé';
$_ADDONLANG['Start'] = 'Démarrer';
$_ADDONLANG['Stop'] = 'Arrêter';
$_ADDONLANG['Restart'] = 'Redémarrer';
$_ADDONLANG['VNC'] = 'Console VNC';
$_ADDONLANG['VNC'] = 'VNC';
$_ADDONLANG['General']['Note'] = 'Note';
$_ADDONLANG['LoadingFirewallRules'] = 'Chargement des règles de pare-feu...';
## Confirmations
$_ADDONLANG['Confirm']['Stop']['Title'] = 'Arreter le VPS';
@@ -33,12 +35,28 @@ $_ADDONLANG['Navbar']['Backups'] = 'Sauvegardes';
$_ADDONLANG['Navbar']['Settings'] = 'Paramètres';
## Overview
$_ADDONLANG['Overview']['ServerInfo'] = 'Informations du serveur';
$_ADDONLANG['Overview']['Hostname'] = 'Nom d\'hôte';
$_ADDONLANG['Overview']['Status'] = 'Statut';
$_ADDONLANG['Overview']['Uptime'] = 'Temps de fonctionnement';
$_ADDONLANG['Overview']['OS'] = 'Système d\'exploitation';
$_ADDONLANG['Overview']['Location'] = 'Emplacement';
$_ADDONLANG['Overview']['ServerType'] = 'Type de serveur';
$_ADDONLANG['Overview']['ResourceAllocation'] = 'Allocation des ressources';
$_ADDONLANG['Overview']['CPU_Cores'] = 'Cœurs CPU';
$_ADDONLANG['Overview']['Memory'] = 'Mémoire';
$_ADDONLANG['Overview']['Storage'] = 'Stockage';
$_ADDONLANG['Overview']['Traffic'] = 'Trafic';
$_ADDONLANG['Overview']['QuickActions'] = 'Actions rapides';
$_ADDONLANG['Overview']['ResourceUsage'] = 'Utilisation des ressources';
$_ADDONLANG['Overview']['CPU'] = 'Utilisation CPU';
$_ADDONLANG['Overview']['CPUUsage'] = 'Utilisation';
$_ADDONLANG['Overview']['RAM'] = 'Utilisation RAM';
$_ADDONLANG['Overview']['Bandwidth'] = 'Utilisation de la bande passante';
$_ADDONLANG['Overview']['Disk'] = 'Espace disque';
$_ADDONLANG['Overview']['DiskSize'] = 'Taille totale';
$_ADDONLANG['Overview']['CPUUsage'] = 'Utilisation CPU';
$_ADDONLANG['General']['GB'] = 'Go';
$_ADDONLANG['General']['TB'] = 'To';
## Graphs
$_ADDONLANG['Graphs']['CPU'] = 'Utilisation CPU';
@@ -101,6 +119,9 @@ $_ADDONLANG['Settings']['Reinstall']['DisableSSHPasswordDesc'] = 'Autoriser uniq
### Firewall
$_ADDONLANG['Settings']['Firewall']['Title'] = 'Pare-feu';
$_ADDONLANG['Settings']['Firewall']['Description'] = 'Les règles sont évaluées de haut en bas. Par défaut, tout est autorisé. Le pare-feu n\'est disponible que sur l\'interface publique. Seul le trafic entrant sera filtré par le pare-feu.';
$_ADDONLANG['Settings']['Firewall']['AddNewRule'] = 'Ajouter une nouvelle règle de pare-feu';
$_ADDONLANG['Settings']['Firewall']['CurrentRules'] = 'Règles de pare-feu actuelles';
$_ADDONLANG['Settings']['Firewall']['AddRule'] = 'Ajouter une règle';
$_ADDONLANG['Settings']['Firewall']['Action'] = 'Action';
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Port';
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocole';
+24 -3
View File
@@ -10,7 +10,9 @@ $_ADDONLANG['Used'] = 'Verwendet';
$_ADDONLANG['Start'] = 'Starten';
$_ADDONLANG['Stop'] = 'Stoppen';
$_ADDONLANG['Restart'] = 'Neustart';
$_ADDONLANG['VNC'] = 'VNC-Konsole';
$_ADDONLANG['VNC'] = 'VNC';
$_ADDONLANG['General']['Note'] = 'Notiz';
$_ADDONLANG['LoadingFirewallRules'] = 'Firewall-Regeln werden geladen...';
## Bestätigungen
$_ADDONLANG['Confirm']['Stop']['Title'] = 'VPS stoppen';
@@ -33,12 +35,28 @@ $_ADDONLANG['Navbar']['Backups'] = 'Sicherungen';
$_ADDONLANG['Navbar']['Settings'] = 'Einstellungen';
## Overview
$_ADDONLANG['Overview']['ServerInfo'] = 'Server-Informationen';
$_ADDONLANG['Overview']['Hostname'] = 'Hostname';
$_ADDONLANG['Overview']['Status'] = 'Status';
$_ADDONLANG['Overview']['Uptime'] = 'Betriebszeit';
$_ADDONLANG['Overview']['OS'] = 'Betriebssystem';
$_ADDONLANG['Overview']['Location'] = 'Standort';
$_ADDONLANG['Overview']['ServerType'] = 'Servertyp';
$_ADDONLANG['Overview']['ResourceAllocation'] = 'Ressourcenzuweisung';
$_ADDONLANG['Overview']['CPU_Cores'] = 'CPU-Kerne';
$_ADDONLANG['Overview']['Memory'] = 'Arbeitsspeicher';
$_ADDONLANG['Overview']['Storage'] = 'Speicher';
$_ADDONLANG['Overview']['Traffic'] = 'Traffic';
$_ADDONLANG['Overview']['QuickActions'] = 'Schnellaktionen';
$_ADDONLANG['Overview']['ResourceUsage'] = 'Ressourcennutzung';
$_ADDONLANG['Overview']['CPU'] = 'CPU-Auslastung';
$_ADDONLANG['Overview']['CPUUsage'] = 'Nutzung';
$_ADDONLANG['Overview']['RAM'] = 'RAM-Auslastung';
$_ADDONLANG['Overview']['Bandwidth'] = 'Bandbreiten-Nutzung';
$_ADDONLANG['Overview']['Bandwidth'] = 'Bandbreite';
$_ADDONLANG['Overview']['Disk'] = 'Festplattenspeicher';
$_ADDONLANG['Overview']['DiskSize'] = 'Gesamtgröße';
$_ADDONLANG['Overview']['CPUUsage'] = 'CPU-Nutzung';
$_ADDONLANG['General']['GB'] = 'GB';
$_ADDONLANG['General']['TB'] = 'TB';
## Graphs
$_ADDONLANG['Graphs']['CPU'] = 'CPU-Auslastung';
@@ -101,6 +119,9 @@ $_ADDONLANG['Settings']['Reinstall']['DisableSSHPasswordDesc'] = 'Wenn aktiviert
### 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 Verkehr wird von der Firewall gefiltert.';
$_ADDONLANG['Settings']['Firewall']['AddNewRule'] = 'Neue Firewall-Regel hinzufügen';
$_ADDONLANG['Settings']['Firewall']['CurrentRules'] = 'Aktuelle Firewall-Regeln';
$_ADDONLANG['Settings']['Firewall']['AddRule'] = 'Regel hinzufügen';
$_ADDONLANG['Settings']['Firewall']['Action'] = 'Aktion';
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Port';
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protokoll';
+24 -3
View File
@@ -10,7 +10,9 @@ $_ADDONLANG['Used'] = 'Utilizzato';
$_ADDONLANG['Start'] = 'Avvia';
$_ADDONLANG['Stop'] = 'Ferma';
$_ADDONLANG['Restart'] = 'Riavvia';
$_ADDONLANG['VNC'] = 'Console VNC';
$_ADDONLANG['VNC'] = 'VNC';
$_ADDONLANG['General']['Note'] = 'Nota';
$_ADDONLANG['LoadingFirewallRules'] = 'Caricamento regole firewall...';
## Conferme
$_ADDONLANG['Confirm']['Stop']['Title'] = 'Ferma VPS';
@@ -33,12 +35,28 @@ $_ADDONLANG['Navbar']['Backups'] = 'Backup';
$_ADDONLANG['Navbar']['Settings'] = 'Impostazioni';
## Overview
$_ADDONLANG['Overview']['ServerInfo'] = 'Informazioni Server';
$_ADDONLANG['Overview']['Hostname'] = 'Hostname';
$_ADDONLANG['Overview']['Status'] = 'Stato';
$_ADDONLANG['Overview']['Uptime'] = 'Tempo di attività';
$_ADDONLANG['Overview']['OS'] = 'Sistema Operativo';
$_ADDONLANG['Overview']['Location'] = 'Posizione';
$_ADDONLANG['Overview']['ServerType'] = 'Tipo di Server';
$_ADDONLANG['Overview']['ResourceAllocation'] = 'Allocazione Risorse';
$_ADDONLANG['Overview']['CPU_Cores'] = 'Core CPU';
$_ADDONLANG['Overview']['Memory'] = 'Memoria';
$_ADDONLANG['Overview']['Storage'] = 'Archiviazione';
$_ADDONLANG['Overview']['Traffic'] = 'Traffico';
$_ADDONLANG['Overview']['QuickActions'] = 'Azioni Rapide';
$_ADDONLANG['Overview']['ResourceUsage'] = 'Utilizzo Risorse';
$_ADDONLANG['Overview']['CPU'] = 'Utilizzo CPU';
$_ADDONLANG['Overview']['CPUUsage'] = 'Utilizzo';
$_ADDONLANG['Overview']['RAM'] = 'Utilizzo RAM';
$_ADDONLANG['Overview']['Bandwidth'] = 'Utilizzo Banda';
$_ADDONLANG['Overview']['Bandwidth'] = 'Banda';
$_ADDONLANG['Overview']['Disk'] = 'Spazio Disco';
$_ADDONLANG['Overview']['DiskSize'] = 'Dimensione Totale';
$_ADDONLANG['Overview']['CPUUsage'] = 'Utilizzo CPU';
$_ADDONLANG['General']['GB'] = 'GB';
$_ADDONLANG['General']['TB'] = 'TB';
## Graphs
$_ADDONLANG['Graphs']['CPU'] = 'Utilizzo CPU';
@@ -101,6 +119,9 @@ $_ADDONLANG['Settings']['Reinstall']['DisableSSHPasswordDesc'] = 'Consenti solo
### 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 sarà filtrato dal firewall.';
$_ADDONLANG['Settings']['Firewall']['AddNewRule'] = 'Aggiungi Nuova Regola Firewall';
$_ADDONLANG['Settings']['Firewall']['CurrentRules'] = 'Regole Firewall Attuali';
$_ADDONLANG['Settings']['Firewall']['AddRule'] = 'Aggiungi Regola';
$_ADDONLANG['Settings']['Firewall']['Action'] = 'Azione';
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Porta';
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocollo';
@@ -8,5 +8,6 @@
* @link https://arkhost.com
*/
header('Location: https://arkhost.com');
http_response_code(403);
exit('Access denied');
?>
@@ -10,7 +10,9 @@ $_ADDONLANG['Used'] = 'Utilizado';
$_ADDONLANG['Start'] = 'Iniciar';
$_ADDONLANG['Stop'] = 'Parar';
$_ADDONLANG['Restart'] = 'Reiniciar';
$_ADDONLANG['VNC'] = 'Consola VNC';
$_ADDONLANG['VNC'] = 'VNC';
$_ADDONLANG['General']['Note'] = 'Nota';
$_ADDONLANG['LoadingFirewallRules'] = 'Carregando regras de firewall...';
## Confirmações
$_ADDONLANG['Confirm']['Stop']['Title'] = 'Parar VPS';
@@ -33,12 +35,28 @@ $_ADDONLANG['Navbar']['Backups'] = 'Cópias de segurança';
$_ADDONLANG['Navbar']['Settings'] = 'Definições';
## Overview
$_ADDONLANG['Overview']['ServerInfo'] = 'Informações do Servidor';
$_ADDONLANG['Overview']['Hostname'] = 'Nome do anfitrião';
$_ADDONLANG['Overview']['Status'] = 'Estado';
$_ADDONLANG['Overview']['Uptime'] = 'Tempo de atividade';
$_ADDONLANG['Overview']['OS'] = 'Sistema Operativo';
$_ADDONLANG['Overview']['Location'] = 'Localização';
$_ADDONLANG['Overview']['ServerType'] = 'Tipo de Servidor';
$_ADDONLANG['Overview']['ResourceAllocation'] = 'Alocação de Recursos';
$_ADDONLANG['Overview']['CPU_Cores'] = 'Núcleos CPU';
$_ADDONLANG['Overview']['Memory'] = 'Memória';
$_ADDONLANG['Overview']['Storage'] = 'Armazenamento';
$_ADDONLANG['Overview']['Traffic'] = 'Tráfego';
$_ADDONLANG['Overview']['QuickActions'] = 'Ações Rápidas';
$_ADDONLANG['Overview']['ResourceUsage'] = 'Utilização de Recursos';
$_ADDONLANG['Overview']['CPU'] = 'Utilização CPU';
$_ADDONLANG['Overview']['CPUUsage'] = 'Utilização';
$_ADDONLANG['Overview']['RAM'] = 'Utilização RAM';
$_ADDONLANG['Overview']['Bandwidth'] = 'Utilização de largura de banda';
$_ADDONLANG['Overview']['Bandwidth'] = 'Largura de banda';
$_ADDONLANG['Overview']['Disk'] = 'Espaço em disco';
$_ADDONLANG['Overview']['DiskSize'] = 'Tamanho total';
$_ADDONLANG['Overview']['CPUUsage'] = 'Utilização CPU';
$_ADDONLANG['General']['GB'] = 'GB';
$_ADDONLANG['General']['TB'] = 'TB';
## Graphs
$_ADDONLANG['Graphs']['CPU'] = 'Utilização CPU';
@@ -101,6 +119,9 @@ $_ADDONLANG['Settings']['Reinstall']['DisableSSHPasswordDesc'] = 'Permitir apena
### 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á apenas disponível na interface pública. Apenas o tráfego de entrada será filtrado pela firewall.';
$_ADDONLANG['Settings']['Firewall']['AddNewRule'] = 'Adicionar Nova Regra de Firewall';
$_ADDONLANG['Settings']['Firewall']['CurrentRules'] = 'Regras de Firewall Atuais';
$_ADDONLANG['Settings']['Firewall']['AddRule'] = 'Adicionar Regra';
$_ADDONLANG['Settings']['Firewall']['Action'] = 'Ação';
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Porta';
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocolo';
+36 -2
View File
@@ -10,7 +10,9 @@ $_ADDONLANG['Used'] = 'Использовано';
$_ADDONLANG['Start'] = 'Запуск';
$_ADDONLANG['Stop'] = 'Остановка';
$_ADDONLANG['Restart'] = 'Перезапуск';
$_ADDONLANG['VNC'] = 'Консоль VNC';
$_ADDONLANG['VNC'] = 'VNC';
$_ADDONLANG['General']['Note'] = 'Примечание';
$_ADDONLANG['LoadingFirewallRules'] = 'Загрузка правил файрвола...';
## Подтверждения
$_ADDONLANG['Confirm']['Stop']['Title'] = 'Остановить VPS';
@@ -33,11 +35,28 @@ $_ADDONLANG['Navbar']['Backups'] = 'Резервные копии';
$_ADDONLANG['Navbar']['Settings'] = 'Настройки';
## Обзор
$_ADDONLANG['Overview']['ServerInfo'] = 'Информация о сервере';
$_ADDONLANG['Overview']['Hostname'] = 'Имя хоста';
$_ADDONLANG['Overview']['Status'] = 'Статус';
$_ADDONLANG['Overview']['Uptime'] = 'Время работы';
$_ADDONLANG['Overview']['OS'] = 'Операционная система';
$_ADDONLANG['Overview']['Location'] = 'Местоположение';
$_ADDONLANG['Overview']['ServerType'] = 'Тип сервера';
$_ADDONLANG['Overview']['ResourceAllocation'] = 'Распределение ресурсов';
$_ADDONLANG['Overview']['CPU_Cores'] = 'Ядра ЦП';
$_ADDONLANG['Overview']['Memory'] = 'Память';
$_ADDONLANG['Overview']['Storage'] = 'Хранилище';
$_ADDONLANG['Overview']['Traffic'] = 'Трафик';
$_ADDONLANG['Overview']['QuickActions'] = 'Быстрые действия';
$_ADDONLANG['Overview']['ResourceUsage'] = 'Использование ресурсов';
$_ADDONLANG['Overview']['CPU'] = 'Использование ЦП';
$_ADDONLANG['Overview']['CPUUsage'] = 'Использование';
$_ADDONLANG['Overview']['RAM'] = 'Использование ОЗУ';
$_ADDONLANG['Overview']['Bandwidth'] = 'Использование пропускной способности';
$_ADDONLANG['Overview']['Bandwidth'] = 'Пропускная способность';
$_ADDONLANG['Overview']['Disk'] = 'Дисковое пространство';
$_ADDONLANG['Overview']['DiskSize'] = 'Общий размер';
$_ADDONLANG['General']['GB'] = 'ГБ';
$_ADDONLANG['General']['TB'] = 'ТБ';
## Графики
$_ADDONLANG['Graphs']['CPU'] = 'Использование ЦП';
@@ -49,6 +68,7 @@ $_ADDONLANG['Graphs']['Day'] = 'День';
$_ADDONLANG['Graphs']['Week'] = 'Неделя';
$_ADDONLANG['Graphs']['Month'] = 'Месяц';
$_ADDONLANG['Graphs']['Year'] = 'Год';
$_ADDONLANG['Graphs']['Loading'] = 'Загрузка графика...';
## Резервные копии
$_ADDONLANG['Backups']['Description'] = 'Ниже перечислены даты, на которые доступны резервные копии этого VPS. Вы можете восстановить или удалить их соответственно.';
@@ -59,6 +79,11 @@ $_ADDONLANG['Backups']['Type'] = 'Тип';
$_ADDONLANG['Backups']['Status'] = 'Статус';
$_ADDONLANG['Backups']['Actions'] = 'Действия';
$_ADDONLANG['Backups']['Create'] = 'Создать резервную копию сейчас';
$_ADDONLANG['Backups']['Available'] = 'Доступна';
$_ADDONLANG['Backups']['Creating'] = 'Создание...';
$_ADDONLANG['Backups']['Error'] = 'Ошибка';
$_ADDONLANG['Backups']['Automatic'] = 'Автоматическая';
$_ADDONLANG['Backups']['Manual'] = 'Ручная';
## Настройки
### Имя хоста
@@ -84,10 +109,19 @@ $_ADDONLANG['Settings']['Reinstall']['Description'] = 'Пожалуйста, у
$_ADDONLANG['Settings']['Reinstall']['OS'] = 'Операционная система';
$_ADDONLANG['Settings']['Reinstall']['Version'] = 'ВЫБЕРИТЕ ВЕРСИЮ';
$_ADDONLANG['Settings']['Reinstall']['Submit'] = 'Переустановить';
$_ADDONLANG['Settings']['Reinstall']['SSHKey'] = 'Публичный SSH-ключ (необязательно)';
$_ADDONLANG['Settings']['Reinstall']['SSHKeyDesc'] = 'Вставьте сюда свой публичный SSH-ключ (начинается с ssh-rsa, ssh-ed25519 и т.д.). Он будет автоматически добавлен в ~/.ssh/authorized_keys для входа без пароля.';
$_ADDONLANG['Settings']['Reinstall']['PostScript'] = 'Скрипт после установки (необязательно)';
$_ADDONLANG['Settings']['Reinstall']['PostScriptDesc'] = 'Введите bash-скрипт, который будет автоматически выполнен после завершения установки ОС. Полезно для установки пакетов, настройки служб или настройки вашей среды.';
$_ADDONLANG['Settings']['Reinstall']['DisableSSHPassword'] = 'Отключить аутентификацию по паролю SSH';
$_ADDONLANG['Settings']['Reinstall']['DisableSSHPasswordDesc'] = 'Если включено, разрешена только аутентификация по SSH-ключу. Вход по паролю через SSH будет отключен для повышенной безопасности.';
### Файрвол
$_ADDONLANG['Settings']['Firewall']['Title'] = 'Файрвол';
$_ADDONLANG['Settings']['Firewall']['Description'] = 'Правила оцениваются сверху вниз. По умолчанию разрешено все. Файрвол доступен только на публичном интерфейсе. Файрвол будет фильтровать только входящий трафик.';
$_ADDONLANG['Settings']['Firewall']['AddNewRule'] = 'Добавить новое правило файрвола';
$_ADDONLANG['Settings']['Firewall']['CurrentRules'] = 'Текущие правила файрвола';
$_ADDONLANG['Settings']['Firewall']['AddRule'] = 'Добавить правило';
$_ADDONLANG['Settings']['Firewall']['Action'] = 'Действие';
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Порт';
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Протокол';
+24 -3
View File
@@ -10,7 +10,9 @@ $_ADDONLANG['Used'] = 'Usado';
$_ADDONLANG['Start'] = 'Iniciar';
$_ADDONLANG['Stop'] = 'Detener';
$_ADDONLANG['Restart'] = 'Reiniciar';
$_ADDONLANG['VNC'] = 'Consola VNC';
$_ADDONLANG['VNC'] = 'VNC';
$_ADDONLANG['General']['Note'] = 'Nota';
$_ADDONLANG['LoadingFirewallRules'] = 'Cargando reglas de firewall...';
## Confirmaciones
$_ADDONLANG['Confirm']['Stop']['Title'] = 'Detener VPS';
@@ -33,12 +35,28 @@ $_ADDONLANG['Navbar']['Backups'] = 'Copias de seguridad';
$_ADDONLANG['Navbar']['Settings'] = 'Configuración';
## Overview
$_ADDONLANG['Overview']['ServerInfo'] = 'Información del Servidor';
$_ADDONLANG['Overview']['Hostname'] = 'Nombre del host';
$_ADDONLANG['Overview']['Status'] = 'Estado';
$_ADDONLANG['Overview']['Uptime'] = 'Tiempo de actividad';
$_ADDONLANG['Overview']['OS'] = 'Sistema Operativo';
$_ADDONLANG['Overview']['Location'] = 'Ubicación';
$_ADDONLANG['Overview']['ServerType'] = 'Tipo de Servidor';
$_ADDONLANG['Overview']['ResourceAllocation'] = 'Asignación de Recursos';
$_ADDONLANG['Overview']['CPU_Cores'] = 'Núcleos de CPU';
$_ADDONLANG['Overview']['Memory'] = 'Memoria';
$_ADDONLANG['Overview']['Storage'] = 'Almacenamiento';
$_ADDONLANG['Overview']['Traffic'] = 'Tráfico';
$_ADDONLANG['Overview']['QuickActions'] = 'Acciones Rápidas';
$_ADDONLANG['Overview']['ResourceUsage'] = 'Uso de Recursos';
$_ADDONLANG['Overview']['CPU'] = 'Uso de CPU';
$_ADDONLANG['Overview']['CPUUsage'] = 'Uso';
$_ADDONLANG['Overview']['RAM'] = 'Uso de RAM';
$_ADDONLANG['Overview']['Bandwidth'] = 'Uso de ancho de banda';
$_ADDONLANG['Overview']['Bandwidth'] = 'Ancho de banda';
$_ADDONLANG['Overview']['Disk'] = 'Espacio en disco';
$_ADDONLANG['Overview']['DiskSize'] = 'Tamaño total';
$_ADDONLANG['Overview']['CPUUsage'] = 'Uso de CPU';
$_ADDONLANG['General']['GB'] = 'GB';
$_ADDONLANG['General']['TB'] = 'TB';
## Graphs
$_ADDONLANG['Graphs']['CPU'] = 'Uso de CPU';
@@ -101,6 +119,9 @@ $_ADDONLANG['Settings']['Reinstall']['DisableSSHPasswordDesc'] = 'Solo permitir
### 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']['AddNewRule'] = 'Agregar Nueva Regla de Firewall';
$_ADDONLANG['Settings']['Firewall']['CurrentRules'] = 'Reglas de Firewall Actuales';
$_ADDONLANG['Settings']['Firewall']['AddRule'] = 'Agregar Regla';
$_ADDONLANG['Settings']['Firewall']['Action'] = 'Acción';
$_ADDONLANG['Settings']['Firewall']['Port'] = 'Puerto';
$_ADDONLANG['Settings']['Firewall']['Protocol'] = 'Protocolo';
@@ -5,10 +5,46 @@
* @link https://arkhost.com
* @author ArkHost <support@arkhost.com>
*}
{* Store VNC hash for later, remove it to prevent page issues *}
<script>
var arkhostVNCHashDetected = (window.location.hash === '#vnc');
if (arkhostVNCHashDetected) {
history.replaceState(null, null, window.location.pathname + window.location.search);
}
</script>
<style>
/* Hide WHMCS default product details except Domain (keep Domain visible to show VPS hostname) */
/* Hide Username, Server Name, IP Address rows using nth-child selectors */
#domain > .row:nth-child(2) {
display: none !important; /* Username */
}
#domain > .row:nth-child(3) {
display: none !important; /* Server Name */
}
#domain > .row:nth-child(4) {
display: none !important; /* IP Address */
}
#domain > br {
display: none !important;
}
#domain > p {
display: none !important; /* Visit Website button */
}
/* Hide original Domain label - will be replaced with Hostname via JavaScript */
#domain > .row:nth-child(1) .col-sm-5 strong {
visibility: hidden;
position: relative;
}
#domain > .row:nth-child(1) .col-sm-5 strong::after {
visibility: visible;
position: absolute;
left: 0;
content: attr(data-hostname-label);
}
.arkhost-vps-container {
font-family: 'Arial', sans-serif;
margin: 15px 0;
@@ -615,7 +651,7 @@
<script type="text/javascript">
var productURL = '{$WEB_ROOT}/clientarea.php?action=productdetails&id={$serviceid}';
var serverInfoInitial = JSON.parse('{$serverInfo|@json_encode}');
var serverInfoInitial = {$serverInfoJson nofilter};
var lang = {
moduleactionfailed: 'Action failed!',
moduleactionsuccess: 'Action completed successfully',
@@ -871,6 +907,16 @@
return false;
}
// Escape untrusted values before inserting them into innerHTML.
function escapeHtml(value) {
return String(value == null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function updateBackupTable(data) {
var tableBody = document.querySelector('#backupTable tbody');
tableBody.innerHTML = '';
@@ -921,8 +967,8 @@
}
row.innerHTML =
'<td>' + backupDate + '</td>' +
'<td>' + (backup.size || 'N/A') + '</td>' +
'<td>' + escapeHtml(backupDate) + '</td>' +
'<td>' + escapeHtml(backup.size || 'N/A') + '</td>' +
'<td>' + backupType + '</td>' +
'<td>' + statusBadge + '</td>' +
'<td style="white-space: nowrap;">' +
@@ -974,6 +1020,28 @@
return false;
}
function addFirewallRule() {
var protocol = document.getElementById('firewallProtocol').value;
var port = document.getElementById('firewallPort').value;
// Validate: Warn if protocol is ANY and port is specified
if (protocol === 'ANY' && port && port.trim() !== '') {
showNotification('Warning: Protocol "ANY" does not support specific port numbers. The port will be ignored by the firewall.', 'warning', 5000);
return false;
}
// Proceed with adding the rule
ArkHostVPS_API('Add Firewall rules', true, {
firewallAction: document.getElementById('firewallAction').value,
protocol: protocol,
source: document.getElementById('firewallSource').value,
port: port,
note: document.getElementById('firewallNote').value
});
return false;
}
function updateFirewallTable(data) {
// Implement firewall table update
var tableBody = document.querySelector('#firewallTable tbody');
@@ -981,7 +1049,7 @@
// Clear existing rules (keep the add row)
var rows = tableBody.querySelectorAll('tr');
for (var i = rows.length - 1; i > 0; i--) {
for (var i = rows.length - 1; i >= 0; i--) {
if (!rows[i].querySelector('#firewallAction')) {
rows[i].remove();
}
@@ -1018,11 +1086,11 @@
if (rule && rule.id) {
var row = document.createElement('tr');
row.innerHTML =
'<td>' + (rule.action || 'N/A') + '</td>' +
'<td>' + (rule.port || 'N/A') + '</td>' +
'<td>' + (rule.protocol || 'N/A') + '</td>' +
'<td>' + (rule.source || 'N/A') + '</td>' +
'<td>' + (rule.note || 'N/A') + '</td>' +
'<td>' + escapeHtml(rule.action || 'N/A') + '</td>' +
'<td>' + escapeHtml(rule.port || 'N/A') + '</td>' +
'<td>' + escapeHtml(rule.protocol || 'N/A') + '</td>' +
'<td>' + escapeHtml(rule.source || 'N/A') + '</td>' +
'<td>' + escapeHtml(rule.note || 'N/A') + '</td>' +
'<td>' +
'<a href="#" onclick="ArkHostVPS_API(\'Delete Firewall rule\', true, { rule_id: \'' + rule.id + '\' }); return false;" title="Delete">' +
'<i class="fa fa-trash text-danger"></i>' +
@@ -1469,76 +1537,6 @@
</div>
<div id="ArkHostVPS">
<div class="title-block text-center dashboard-title mb-3">{$_LANG['Title']}</div>
<div class="row mb-3">
<div class="col-lg-6 col-sm-6 col-md-12 mb-3 text-center">
<div class="border p-2">
<div class="mb-2">
<span><img src="{$serverInfo['operatingSystem']['image']}" width="48px" height="48px" alt="{$serverInfo['operatingSystem']['name']}" style="margin-top: 8px;"/></span>
</div>
<div class="information">
<span class="form-label dashboard-value d-inline-block mb-2">{$serverInfo['operatingSystem']['name']}</span>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-6 col-md-12 mb-3 text-center">
<div class="border p-2">
<div class="mb-2">
<span><img src="{$serverInfo['statusImage']}" height="48px" style="margin-top: 8px;"/></span>
</div>
<div class="information">
<span class="form-label dashboard-value d-inline-block mb-2">{$serverInfo['statusDescription']}</span>
</div>
</div>
</div>
<div class="col-lg-12 col-sm-12 col-md-12 mb-3">
<div class="border p-2">
<div class="row w-100">
<div class="col-4 text-center">
<a href="#"
onclick="{if $serverInfo['status'] !== 'running'}return ArkHostVPS_API('Start');{else}return confirmStop();{/if}"
class="vps-control-btn {if $serverInfo['status'] !== 'running'}start-btn{else}stop-btn{/if}"
title="{if $serverInfo['status'] !== 'running'}{$_LANG['Start']}{else}{$_LANG['Stop']}{/if}">
<i class="fa fa-{if $serverInfo['status'] !== 'running'}play{else}stop{/if}" aria-hidden="true"></i>
{if $serverInfo['status'] !== 'running'}{$_LANG['Start']}{else}{$_LANG['Stop']}{/if}
</a>
</div>
<div class="col-4 text-center">
<a href="#"
onclick="return confirmRestart();"
class="vps-control-btn restart-btn"
title="{$_LANG['Restart']}">
<i class="fa fa-sync" aria-hidden="true"></i>
{$_LANG['Restart']}
</a>
</div>
<div class="col-4 text-center">
<a href="{$WEB_ROOT}/clientarea.php?action=productdetails&id={$serviceid}&modop=custom&a=VNC"
target="_blank"
class="vps-control-btn vnc-btn"
title="{$_LANG['VNC']}">
<img src="{$images['vnc']}" alt="VNC Console"/>
{$_LANG['VNC']}
</a>
</div>
<div class="col-md-12" style="margin-top: 5px;">
<label class="form-label d-inline-block;">{$_LANG['Uptime']}:</label>
<span class="form-label dashboard-value d-inline-block mr-2">{$serverInfo['uptime_text']}</span>
<br />
<label class="form-label d-inline-block;">{$_LANG['IPv4']}:</label>
<span class="form-label dashboard-value d-inline-block mr-2">{$serverInfo['ip']}</span>
{if $serverInfo['ipv6']}
<br />
<label class="form-label d-inline-block;">{$_LANG['IPv6']}:</label>
<span class="form-label dashboard-value d-inline-block mr-2">{$serverInfo['ipv6']}</span>
{/if}
</div>
</div>
</div>
</div>
</div>
<div class="dashboard-tab" id="dashboard">
<ul class="nav nav-tabs mb-4 dash-tabs" id="pills-tab" role="tablist">
<li class="nav-item">
@@ -1556,21 +1554,132 @@
<i class="fa fa-archive"></i> {$_LANG['Navbar']['Backups']}
</a>
</li>
<li class="nav-item">
<a class="nav-link" id="firewall-tab" data-toggle="tab" href="#firewall" onclick="ArkHostVPS_API('Get Firewall rules', false);return false;" role="tab" aria-controls="firewall" aria-selected="false">
<i class="fa fa-shield-alt"></i> {$_LANG['Settings']['Firewall']['Title']}
</a>
</li>
<li class="nav-item">
<a class="nav-link" id="settings-tab" data-toggle="tab" href="#settings" role="tab" aria-controls="settings" aria-selected="false">
<i class="fa fa-cog"></i> {$_LANG['Navbar']['Settings']}
</a>
</li>
{if $vncMode == 'embedded'}
<li class="nav-item">
<a class="nav-link" id="vnc-tab" data-toggle="tab" href="#vnc" role="tab" aria-controls="vnc" aria-selected="false" onclick="loadVNCConsole()">
<i class="fa fa-desktop"></i> {$_LANG['VNC']}
</a>
</li>
{/if}
</ul>
<div class="tab-content" id="pills-tabContent">
<div class="tab-pane fade show active" id="overview" role="tabpanel" aria-labelledby="overview-tab">
<div class="head text-center mb-3">
<img src="{$images['eye']}">
<span class="h4">{$_LANG['Navbar']['Overview']}</span>
<!-- Server Status Card -->
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<h5 class="mb-0"><i class="fa fa-server mr-2"></i>{$_LANG['Overview']['ServerInfo']}</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<table class="table table-sm table-borderless mb-0">
<tr>
<td class="text-muted" width="40%">{$_LANG['Overview']['Hostname']}:</td>
<td><strong>{$serverInfo['hostname']|default:'N/A'|escape}</strong></td>
</tr>
<tr>
<td class="text-muted">{$_LANG['IPv4']} Address:</td>
<td><code>{$serverInfo['ip']|escape}</code></td>
</tr>
{if $serverInfo['ipv6']}
<tr>
<td class="text-muted">{$_LANG['IPv6']} Address:</td>
<td><code style="font-size: 11px;">{$serverInfo['ipv6']|escape}</code></td>
</tr>
{/if}
<tr>
<td class="text-muted">{$_LANG['Overview']['OS']}:</td>
<td>
{if $serverInfo['operatingSystem']['image']}
<img src="{$serverInfo['operatingSystem']['image']}" width="20" height="20" class="mr-1" style="vertical-align: middle;">
{/if}
{$serverInfo['operatingSystem']['name']|default:'N/A'|escape}
</td>
</tr>
</table>
</div>
<div class="col-md-6">
<table class="table table-sm table-borderless mb-0">
<tr>
<td class="text-muted" width="40%">{$_LANG['Overview']['Status']}:</td>
<td>
<img src="{$serverInfo['statusImage']}" height="20" class="mr-1" style="vertical-align: middle;">
<span class="{if $serverInfo['status'] == 'running'}text-success{else}text-danger{/if}">
<strong>{$serverInfo['statusDescription']|escape}</strong>
</span>
</td>
</tr>
<tr>
<td class="text-muted">{$_LANG['Overview']['Uptime']}:</td>
<td><strong>{$serverInfo['uptime_text']|default:'N/A'|escape}</strong></td>
</tr>
<tr>
<td class="text-muted">{$_LANG['Overview']['ServerType']}:</td>
<td><strong>{$serverInfo['server_type']|default:'Standard'|escape}</strong></td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="row mb-2">
<div class="col-lg-3 col-sm-6 col-md-6 mb-3">
<!-- Resource Allocation Card -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="fa fa-microchip mr-2"></i>{$_LANG['Overview']['ResourceAllocation']}</h5>
</div>
<div class="card-body">
<div class="row text-center">
<div class="col-6 col-md-3 mb-3">
<div class="resource-box">
<i class="fa fa-microchip fa-2x text-primary mb-2"></i>
<h3 class="mb-0">{$serverInfo['cpu']|default:'N/A'}</h3>
<small class="text-muted">{$_LANG['Overview']['CPU_Cores']}</small>
</div>
</div>
<div class="col-6 col-md-3 mb-3">
<div class="resource-box">
<i class="fa fa-memory fa-2x text-success mb-2"></i>
<h3 class="mb-0">{$serverInfo['ram']|default:'N/A'} {$_LANG['General']['GB']}</h3>
<small class="text-muted">{$_LANG['Overview']['Memory']}</small>
</div>
</div>
<div class="col-6 col-md-3 mb-3">
<div class="resource-box">
<i class="fa fa-hdd fa-2x text-warning mb-2"></i>
<h3 class="mb-0">{$serverInfo['disk']|default:'N/A'} {$_LANG['General']['GB']}</h3>
<small class="text-muted">{$_LANG['Overview']['Storage']}</small>
</div>
</div>
<div class="col-6 col-md-3 mb-3">
<div class="resource-box">
<i class="fa fa-network-wired fa-2x text-info mb-2"></i>
<h3 class="mb-0">{$serverInfo['bandwidth']|default:'20'} {$_LANG['General']['TB']}</h3>
<small class="text-muted">{$_LANG['Overview']['Traffic']}</small>
</div>
</div>
</div>
</div>
</div>
<!-- Resource Usage Card -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="fa fa-chart-bar mr-2"></i>{$_LANG['Overview']['ResourceUsage']}</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-lg-4 col-sm-6 col-md-6 mb-3">
<div class="usage-details px-3 py-4" style="height: 140px; display: flex; flex-direction: column; justify-content: space-between;">
<p class="overview-label">
{$_LANG['Overview']['RAM']}
@@ -1589,10 +1698,10 @@
</div>
</div>
</div>
<div class="col-lg-3 col-sm-6 col-md-6 mb-3">
<div class="col-lg-4 col-sm-6 col-md-6 mb-3">
<div class="usage-details px-3 py-4" style="height: 140px; display: flex; flex-direction: column; justify-content: space-between;">
<p class="overview-label">
{$_LANG['Overview']['Bandwidth']}
Bandwidth
<button onclick="refreshBandwidthUsage();return false;" class="btn btn-sm btn-outline-secondary ml-2" style="padding: 2px 6px; font-size: 10px; border-radius: 50%; width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center;" title="Refresh Bandwidth Usage">
<i class="fa fa-sync-alt" style="font-size: 8px;"></i>
</button>
@@ -1608,21 +1717,7 @@
</div>
</div>
</div>
<div class="col-lg-3 col-sm-6 col-md-6 mb-3">
<div class="usage-details px-3 py-4" style="height: 140px; display: flex; flex-direction: column; justify-content: space-between;">
<p class="overview-label">{$_LANG['Overview']['Disk']}</p>
<div class="progress disk-bar">
<div class="progress-bar text-center w-100 d-flex align-items-center justify-content-center" style="background: #ffc107; font-weight: bold; color: white;">
<span id="diskPercentVal">{$serverInfo['disk']} GB</span>
</div>
</div>
<div>
<span class="used_disk_percent mr-1">{$_LANG['Overview']['DiskSize']}</span>
<span class="overview-label"></span>
</div>
</div>
</div>
<div class="col-lg-3 col-sm-6 col-md-6 mb-3">
<div class="col-lg-4 col-sm-6 col-md-6 mb-3">
<div class="usage-details px-3 py-4" style="height: 140px; display: flex; flex-direction: column; justify-content: space-between;">
<p class="overview-label">
{$_LANG['Overview']['CPU']}
@@ -1643,6 +1738,39 @@
</div>
</div>
</div>
</div>
<!-- Quick Actions Card -->
<div class="card">
<div class="card-header">
<h5 class="mb-0"><i class="fa fa-bolt mr-2"></i>{$_LANG['Overview']['QuickActions']}</h5>
</div>
<div class="card-body text-center">
<div class="d-flex justify-content-center flex-wrap" style="gap: 10px;">
<button class="btn btn-{if $serverInfo['status'] !== 'running'}success{else}danger{/if}"
onclick="{if $serverInfo['status'] !== 'running'}return ArkHostVPS_API('Start');{else}return confirmStop();{/if}">
<i class="fa fa-{if $serverInfo['status'] !== 'running'}play{else}stop{/if} mr-1"></i>
{if $serverInfo['status'] !== 'running'}{$_LANG['Start']}{else}{$_LANG['Stop']}{/if}
</button>
<button class="btn btn-primary"
onclick="return confirmRestart();">
<i class="fa fa-sync mr-1"></i> {$_LANG['Restart']}
</button>
{if $vncMode == 'embedded'}
<button class="btn btn-info" onclick="$('#vnc-tab').tab('show'); loadVNCConsole();">
<i class="fa fa-desktop mr-1"></i> {$_LANG['VNC']}
</button>
{else}
<a href="{$WEB_ROOT}/clientarea.php?action=productdetails&id={$serviceid}&modop=custom&a=VNC"
target="_blank"
class="btn btn-info">
<i class="fa fa-desktop mr-1"></i> {$_LANG['VNC']}
</a>
{/if}
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="graphs" role="tabpanel" aria-labelledby="graphs-tab">
<div class="head text-center mb-3">
@@ -1759,6 +1887,96 @@
</div>
</div>
<div class="tab-pane fade" id="firewall" role="tabpanel" aria-labelledby="firewall-tab">
<div class="alert alert-info mb-4">
<i class="fa fa-info-circle mr-2"></i><strong>{$_LANG['General']['Note']}:</strong> {$_LANG['Settings']['Firewall']['Description']}
</div>
<!-- Add New Rule Card -->
<div class="card mb-4 border-primary">
<div class="card-header bg-light">
<h6 class="mb-0"><i class="fa fa-plus-circle mr-2"></i>{$_LANG['Settings']['Firewall']['AddNewRule']}</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-2">
<label class="small text-muted">{$_LANG['Settings']['Firewall']['Action']}</label>
<select class="form-control" id="firewallAction">
<option value="ACCEPT" class="text-success">✓ {$_LANG['Settings']['Firewall']['Accept']}</option>
<option value="DROP" class="text-danger">✗ {$_LANG['Settings']['Firewall']['Drop']}</option>
</select>
</div>
<div class="col-md-2">
<label class="small text-muted">{$_LANG['Settings']['Firewall']['Protocol']}</label>
<select class="form-control" id="firewallProtocol">
<option value="ANY">ANY</option>
<option value="ICMP">ICMP</option>
<option value="TCP">TCP</option>
<option value="UDP">UDP</option>
</select>
</div>
<div class="col-md-2">
<label class="small text-muted">{$_LANG['Settings']['Firewall']['Port']}</label>
<input class="form-control" id="firewallPort" type="number" min="1" max="65535" placeholder="{$_LANG['Settings']['Firewall']['Port']}">
</div>
<div class="col-md-3">
<label class="small text-muted">{$_LANG['Settings']['Firewall']['SourceLabel']}</label>
<input class="form-control" id="firewallSource" type="text" maxlength="128" placeholder="{$_LANG['Settings']['Firewall']['SourceLabel']}">
</div>
<div class="col-md-3">
<label class="small text-muted">{$_LANG['Settings']['Firewall']['Note']}</label>
<input class="form-control" id="firewallNote" type="text" maxlength="64" placeholder="{$_LANG['Settings']['Firewall']['Notes']}">
</div>
</div>
<div class="text-right mt-3">
<button class="btn btn-success" onclick="return addFirewallRule();">
<i class="fa fa-plus mr-2"></i>{$_LANG['Settings']['Firewall']['AddRule']}
</button>
</div>
</div>
</div>
<!-- Current Rules -->
<div class="card">
<div class="card-header bg-light">
<h6 class="mb-0"><i class="fa fa-list mr-2"></i>{$_LANG['Settings']['Firewall']['CurrentRules']}</h6>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table id="firewallTable" class="table table-hover mb-0">
<thead class="thead-light">
<tr>
<th width="100">{$_LANG['Settings']['Firewall']['Action']}</th>
<th width="100">{$_LANG['Settings']['Firewall']['Protocol']}</th>
<th width="100">{$_LANG['Settings']['Firewall']['Port']}</th>
<th>{$_LANG['Settings']['Firewall']['Source']}</th>
<th width="150">{$_LANG['Settings']['Firewall']['Note']}</th>
<th width="80" class="text-center">{$_LANG['Settings']['Firewall']['Actions']}</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="6" class="text-center text-muted py-4">
<i class="fa fa-spinner fa-spin mr-2"></i>{$_LANG['LoadingFirewallRules']}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Commit button -->
<div class="alert alert-warning mt-4 mb-4">
<i class="fa fa-exclamation-triangle mr-2"></i><strong>{$_LANG['General']['Note']}:</strong> {$_LANG['Settings']['Firewall']['Warning']}
</div>
<div class="text-center">
<button onclick="ArkHostVPS_API('Commit Firewall rules');return false;" class="btn btn-primary btn-lg">
<i class="fa fa-check mr-2"></i>{$_LANG['Settings']['Firewall']['Submit']}
</button>
</div>
</div>
<div class="tab-pane fade" id="settings" role="tabpanel" aria-labelledby="settings-tab">
<div class="row vertical-tabs">
<div class="col-12 col-sm-3 col-md-3 col-lg-3 v-tabs-container">
@@ -1767,7 +1985,6 @@
<a class="nav-link mr-2 mr-md-0" id="iso-tab" data-toggle="tab" href="#iso" onclick="ArkHostVPS_API('ISO Images', false);" role="tab" aria-controls="iso" aria-selected="false" style="display: block;">{$_LANG['Settings']['ISO']['Title']}</a>
<a class="nav-link mr-2 mr-md-0" id="password-tab" data-toggle="tab" href="#password" role="tab" aria-controls="password" aria-selected="false" style="display: block;">{$_LANG['Settings']['Password']['Title']}</a>
<a class="nav-link mr-2 mr-md-0" id="reinstall-tab" data-toggle="tab" href="#reinstall" role="tab" aria-controls="reinstall" aria-selected="false" style="display: block;">{$_LANG['Settings']['Reinstall']['Title']}</a>
<a class="nav-link mr-2 mr-md-0" id="firewall-tab" data-toggle="tab" href="#firewall" onclick="ArkHostVPS_API('Get Firewall rules', false);" role="tab" aria-controls="firewall" aria-selected="false" style="display: block;">{$_LANG['Settings']['Firewall']['Title']}</a>
</div>
</div>
<div class="col-12 col-sm-9 col-md-9 col-lg-9">
@@ -1786,7 +2003,7 @@
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">{$_LANG['Settings']['Hostname']['Title']}:</label>
<input class="form-control" id="hostnameRDNS" type="text" size="30" maxlength="128" value="{$serverInfo['hostname']}">
<input class="form-control" id="hostnameRDNS" type="text" size="30" maxlength="128" value="{$serverInfo['hostname']|escape}">
</div>
</div>
</div>
@@ -1838,7 +2055,7 @@
<label class="form-label">{$_LANG['Settings']['Password']['Title']}:</label>
<div class="input-group mb-3">
<input class="form-control" id="vpsPassword" type="password" size="30" maxlength="128" disabled value="{if $serverInfo['install_root'] != ''}{$serverInfo['install_root']}{else}Expired{/if}">
<input class="form-control" id="vpsPassword" type="password" size="30" maxlength="128" disabled value="{if $serverInfo['install_root'] != ''}{$serverInfo['install_root']|escape}{else}Expired{/if}">
<button class="btn btn-outline-secondary" type="button" onclick="ArkHostVPS_ShowPassword();return false;">
<i class="fa fa-eye" id="showPasswordIcon" aria-hidden="true"></i>
</button>
@@ -1873,14 +2090,14 @@
</div>
<div class="media-body float-left text-left p-2">
<h6 class="distro_name media-heading">{$operatingSystemsGroup['name']}</h6>
<h6 class="distro_name media-heading">{$operatingSystemsGroup['name']|escape}</h6>
<span id="{$group}-version" class="version small">{$_LANG['Settings']['Reinstall']['Version']}</span>
</div>
</button>
<div class="os_badge_list dropdown-menu w-100">
{foreach from=$operatingSystemsGroup['versions'] item=$operatingSystem}
<a class="dropdown-item" href="#" data-os="{$operatingSystem['id']}" data-group="{$group}" onclick="ArkHostVPS_ChooseOS(this);return false;">{$operatingSystem['name']}</a>
<a class="dropdown-item" href="#" data-os="{$operatingSystem['id']|escape}" data-group="{$group|escape}" onclick="ArkHostVPS_ChooseOS(this);return false;">{$operatingSystem['name']|escape}</a>
{/foreach}
</div>
</div>
@@ -1923,71 +2140,36 @@
<button onclick="reinstallWithAdvancedOptions();" class="submit-btn">{$_LANG['Settings']['Reinstall']['Submit']}</button>
</div>
</div>
<div class="tab-pane fade" id="firewall" role="tabpanel" aria-labelledby="firewall-tab">
<div class="head">
<img src="{$images['settings']}" style="width: 32px; height: 32px;">
<span class="h4">{$_LANG['Settings']['Firewall']['Title']}</span>
</div>
</div>
</div>
</div>
<div class="alert alert-info mb-3">
{$_LANG['Settings']['Firewall']['Description']}
{if $vncMode == 'embedded'}
<!-- VNC Tab -->
<div class="tab-pane fade" id="vnc" role="tabpanel" aria-labelledby="vnc-tab">
<div class="card" id="vncCard">
<div class="card-header bg-info text-white d-flex justify-content-between align-items-center">
<h5 class="mb-0"><i class="fa fa-desktop mr-2"></i>{$_LANG['VNC']}</h5>
<div>
<button class="btn btn-sm btn-light" onclick="openVNCNewTab()" title="Open in New Tab">
<i class="fa fa-external-link-alt"></i>
</button>
<button class="btn btn-sm btn-light ml-1" onclick="toggleVNCFullscreen()" title="Fullscreen" id="vncFullscreenBtn">
<i class="fa fa-expand"></i>
</button>
</div>
<div class="table-responsive">
<table id="firewallTable" cellpadding="0" cellspacing="0" border="0" class="table table-hover" width="100%">
<thead>
<tr>
<th>{$_LANG['Settings']['Firewall']['Action']}</th>
<th>{$_LANG['Settings']['Firewall']['Port']}</th>
<th>{$_LANG['Settings']['Firewall']['Protocol']}</th>
<th>{$_LANG['Settings']['Firewall']['Source']}</th>
<th>{$_LANG['Settings']['Firewall']['Note']}</th>
<th width="50">{$_LANG['Settings']['Firewall']['Actions']}</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<select class="form-control form-control-sm" id="firewallAction">
<option value="ACCEPT">{$_LANG['Settings']['Firewall']['Accept']}</option>
<option value="DROP">{$_LANG['Settings']['Firewall']['Drop']}</option>
</select>
</td>
<td>
<input class="form-control form-control-sm" id="firewallPort" type="number" min="1" max="65535" placeholder="{$_LANG['Settings']['Firewall']['Port']}">
</td>
<td>
<select class="form-control form-control-sm" id="firewallProtocol">
<option value="ANY">ANY</option>
<option value="ICMP">ICMP</option>
<option value="TCP">TCP</option>
<option value="UDP">UDP</option>
</select>
</td>
<td>
<input class="form-control form-control-sm" id="firewallSource" type="text" maxlength="128" placeholder="{$_LANG['Settings']['Firewall']['SourceLabel']}">
</td>
<td>
<input class="form-control form-control-sm" id="firewallNote" type="text" maxlength="64" placeholder="{$_LANG['Settings']['Firewall']['Notes']}">
</td>
<td>
<a href="#" onclick="ArkHostVPS_API('Add Firewall rules', true, { firewallAction: document.getElementById('firewallAction').value, protocol: document.getElementById('firewallProtocol').value, source: document.getElementById('firewallSource').value, port: document.getElementById('firewallPort').value, note: document.getElementById('firewallNote').value });return false;"><i class="fas fa-1x fa-plus text-success" aria-hidden="true"></i></a>
</td>
</tr>
</tbody>
</table>
</div>
<div class="alert alert-warning">
{$_LANG['Settings']['Firewall']['Warning']}
</div>
<button onclick="ArkHostVPS_API('Commit Firewall rules');return false;" class="submit-btn">{$_LANG['Settings']['Firewall']['Submit']}</button>
</div>
<div class="card-body p-0" id="vncContainer">
<div id="vncLoading" class="text-center p-5">
<i class="fa fa-spinner fa-spin fa-3x"></i>
<p class="mt-3">Loading VNC Console...</p>
</div>
<iframe id="vncIframe" src="" sandbox="allow-scripts allow-same-origin allow-forms allow-pointer-lock" style="width: 100%; height: 600px; border: none; display: none;"></iframe>
</div>
</div>
</div>
{/if}
</div>
</div>
</div>
@@ -1997,5 +2179,158 @@
// Wait for WHMCS jQuery to be ready and use it
jQuery(document).ready(function($) {
// WHMCS already loads Bootstrap and Font Awesome - no need to load additional libraries
// Replace "Domain" label with translated "Hostname" label
var hostnameLabel = '{$_LANG.Overview.Hostname|escape:"javascript"}';
$('#domain > .row:nth-child(1) .col-sm-5 strong').attr('data-hostname-label', hostnameLabel);
// Intercept sidebar VPS action button clicks to show confirmation popups
// Use capture phase to intercept before other handlers
$('.list-group-item').each(function() {
var $link = $(this);
var href = $link.attr('href');
if (!href) return;
// Remove any existing click handlers and add our own
if (href.indexOf('modop=custom&a=Start') !== -1) {
$link.off('click').on('click', function(e) {
e.preventDefault();
e.stopImmediatePropagation();
ArkHostVPS_API('Start');
return false;
});
} else if (href.indexOf('modop=custom&a=Stop') !== -1) {
$link.off('click').on('click', function(e) {
e.preventDefault();
e.stopImmediatePropagation();
confirmStop();
return false;
});
} else if (href.indexOf('modop=custom&a=Reboot') !== -1) {
$link.off('click').on('click', function(e) {
e.preventDefault();
e.stopImmediatePropagation();
confirmRestart();
return false;
});
}
});
// Handle hash navigation for VNC tab (when coming from sidebar)
// Only run if embedded VNC mode is enabled (vnc tab exists)
if ($('#vnc-tab').length > 0) {
// Check on page load - use the stored variable from top of page
if (typeof arkhostVNCHashDetected !== 'undefined' && arkhostVNCHashDetected) {
setTimeout(function() {
$('#vnc-tab').tab('show');
if (typeof loadVNCConsole === 'function') {
loadVNCConsole();
}
}, 300);
}
// Listen for hash changes
$(window).on('hashchange', function() {
if (window.location.hash === '#vnc') {
$('#vnc-tab').tab('show');
if (typeof loadVNCConsole === 'function') {
loadVNCConsole();
}
}
});
// Intercept sidebar VNC link clicks
$('a[href$="#vnc"]').on('click', function(e) {
e.preventDefault();
$('#vnc-tab').tab('show');
if (typeof loadVNCConsole === 'function') {
loadVNCConsole();
}
});
}
});
// VNC Tab functions
var vncLoaded = false;
function loadVNCConsole() {
if (vncLoaded) return;
var iframe = document.getElementById('vncIframe');
var loading = document.getElementById('vncLoading');
var container = document.getElementById('vncContainer');
if (!iframe || !container) return;
// Fetch VNC URL via API and load directly
jQuery.get(productURL + '&modop=custom&a=ClientAreaAPI&api=VNC Console&token={$token}', function(data) {
console.log('VNC API response:', data);
if (data && data.vnc_url) {
iframe.src = data.vnc_url;
iframe.style.display = 'block';
if (loading) loading.style.display = 'none';
vncLoaded = true;
} else if (data && data.message) {
container.innerHTML = '<div class="alert alert-danger m-3">' + data.message + '</div>';
} else {
container.innerHTML = '<div class="alert alert-warning m-3">VNC URL not available. Response: ' + JSON.stringify(data) + '</div>';
}
}).fail(function(xhr, status, error) {
console.log('VNC API error:', status, error);
container.innerHTML = '<div class="alert alert-danger m-3">Failed to load VNC console: ' + error + '</div>';
});
}
// Open VNC in new tab - fetch URL via API to avoid session issues
function openVNCNewTab() {
jQuery.get(productURL + '&modop=custom&a=ClientAreaAPI&api=VNC Console&token={$token}', function(data) {
if (data && data.vnc_url) {
window.open(data.vnc_url, '_blank');
} else {
alert('Failed to get VNC URL');
}
}).fail(function() {
alert('Failed to get VNC URL');
});
}
// Toggle fullscreen for VNC
function toggleVNCFullscreen() {
var card = document.getElementById('vncCard');
var iframe = document.getElementById('vncIframe');
var btn = document.getElementById('vncFullscreenBtn');
if (!document.fullscreenElement) {
if (card.requestFullscreen) {
card.requestFullscreen();
} else if (card.webkitRequestFullscreen) {
card.webkitRequestFullscreen();
} else if (card.msRequestFullscreen) {
card.msRequestFullscreen();
}
iframe.style.height = '100vh';
btn.innerHTML = '<i class="fa fa-compress"></i>';
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
iframe.style.height = '600px';
btn.innerHTML = '<i class="fa fa-expand"></i>';
}
}
// Handle fullscreen change events
document.addEventListener('fullscreenchange', function() {
var iframe = document.getElementById('vncIframe');
var btn = document.getElementById('vncFullscreenBtn');
if (!document.fullscreenElement && iframe && btn) {
iframe.style.height = '600px';
btn.innerHTML = '<i class="fa fa-expand"></i>';
}
});
</script>
@@ -617,3 +617,424 @@ input[type=password] {
width:100%;
}
}
/* ============================================
Enhanced Modern Styling
============================================ */
/* Card Styling */
.card {
border-radius: 12px !important;
border: 1px solid #e3e6f0 !important;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.card-header {
border-radius: 12px 12px 0 0 !important;
background-color: #f8f9fa !important;
border-bottom: 1px solid #e3e6f0 !important;
padding: 12px 20px !important;
}
.card-body {
padding: 20px !important;
}
/* Enhanced Button Styles */
.btn {
border-radius: 12px !important;
font-weight: 500;
transition: all 0.3s ease;
padding: 10px 20px;
}
.btn-success {
background: linear-gradient(135deg, #28a745 0%, #20c997 100%) !important;
border: none !important;
box-shadow: 0 2px 8px rgba(40, 167, 69, 0.3);
}
.btn-success:hover {
background: linear-gradient(135deg, #218838 0%, #1aa179 100%) !important;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(40, 167, 69, 0.4);
}
.btn-primary {
background: linear-gradient(135deg, #3c7fb4 0%, #2d5a87 100%) !important;
border: none !important;
box-shadow: 0 2px 8px rgba(60, 127, 180, 0.3);
}
.btn-primary:hover {
background: linear-gradient(135deg, #2d5a87 0%, #1e3a57 100%) !important;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(60, 127, 180, 0.4);
}
.btn-danger {
background: linear-gradient(135deg, #dc3545 0%, #c82333 100%) !important;
border: none !important;
box-shadow: 0 2px 8px rgba(220, 53, 69, 0.3);
}
.btn-danger:hover {
background: linear-gradient(135deg, #c82333 0%, #bd2130 100%) !important;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(220, 53, 69, 0.4);
}
.btn-lg {
padding: 12px 32px !important;
font-size: 16px !important;
}
/* Improved submit-btn to match Hetzner style */
.submit-btn {
background: linear-gradient(135deg, #3c7fb4 0%, #2d5a87 100%) !important;
border: none !important;
border-radius: 12px !important;
color: #ffffff !important;
font-weight: 600 !important;
padding: 10px 20px !important;
transition: all 0.3s ease !important;
box-shadow: 0 2px 8px rgba(60, 127, 180, 0.3);
}
.submit-btn:hover {
background: linear-gradient(135deg, #2d5a87 0%, #1e3a57 100%) !important;
color: #ffffff !important;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(60, 127, 180, 0.4) !important;
}
/* Table Styling */
.table {
border-collapse: separate;
border-spacing: 0;
}
.table thead th {
border-bottom: 2px solid #dee2e6 !important;
color: #495057;
font-weight: 600;
text-transform: uppercase;
font-size: 0.85rem;
letter-spacing: 0.5px;
}
.table-hover tbody tr {
transition: all 0.2s ease;
}
.table-hover tbody tr:hover {
background-color: #f8f9fa;
transform: scale(1.01);
}
.thead-light th {
background-color: #f8f9fa !important;
}
/* Form Control Improvements */
.form-control {
border-radius: 8px !important;
border: 1px solid #ced4da !important;
transition: all 0.2s ease;
padding: 8px 12px;
}
.form-control:focus {
border-color: #3c7fb4 !important;
box-shadow: 0 0 0 0.2rem rgba(60, 127, 180, 0.25) !important;
}
/* Alert Styling */
.alert {
border-radius: 12px !important;
border: none !important;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.alert-info {
background: linear-gradient(135deg, #e7f3ff 0%, #d4e9ff 100%);
color: #004085;
}
.alert-warning {
background: linear-gradient(135deg, #fff3cd 0%, #ffe5a1 100%);
color: #856404;
}
.alert-success {
background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%);
color: #155724;
}
/* Firewall Specific Styles */
#firewall .card.border-primary {
border: 2px solid #3c7fb4 !important;
box-shadow: 0 4px 16px rgba(60, 127, 180, 0.15);
}
#firewall .card-header.bg-light {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%) !important;
}
#firewall .card-header h6 {
margin: 0;
color: #2c3e50;
font-weight: 600;
}
/* Delete Icon Styling */
.fa-trash {
transition: all 0.2s ease;
cursor: pointer;
}
.fa-trash:hover {
transform: scale(1.2);
color: #dc3545 !important;
}
/* Loading Spinner */
.fa-spinner {
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Smooth transitions for tab content */
.tab-pane {
animation: fadeIn 0.3s ease-in;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Nav tabs enhancement */
.nav-tabs .nav-link {
border-radius: 12px 12px 0 0 !important;
transition: all 0.2s ease;
}
.nav-tabs .nav-link:hover {
background-color: #f8f9fa;
}
.nav-tabs .nav-link.active {
background-color: #e9ecef !important;
font-weight: 600;
}
/* Icon improvements */
.fa, .fas, .far {
vertical-align: middle;
}
/* Small label styling */
label.small {
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 6px;
}
/* Text center improvements */
.text-center {
text-align: center;
}
/* Resource Box Styling */
.resource-box {
padding: 20px 10px;
transition: all 0.3s ease;
}
.resource-box:hover {
transform: translateY(-5px);
}
.resource-box h3 {
color: #2c3e50;
font-weight: 600;
}
.resource-box .text-primary {
color: #3c7fb4 !important;
}
.resource-box .text-success {
color: #28a745 !important;
}
.resource-box .text-warning {
color: #ffc107 !important;
}
.resource-box .text-info {
color: #17a2b8 !important;
}
/* Card Header Enhancement */
.card-header.bg-primary {
background: linear-gradient(135deg, #3c7fb4 0%, #2d5a87 100%) !important;
}
.card-header h5 {
margin: 0;
font-weight: 600;
}
/* Table Enhancements */
.table-borderless td {
padding: 8px 12px;
vertical-align: middle;
}
.table-borderless code {
background-color: #f8f9fa;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
color: #e83e8c;
}
/* Usage Details Box Enhancement */
.usage-details {
border-radius: 12px !important;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
}
.usage-details:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
transform: translateY(-2px);
}
/* VPS Control Button Styles - Hetzner Style */
.vps-control-btn {
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
padding: 12px 24px !important;
border-radius: 25px !important;
margin: 8px 4px !important;
transition: all 0.3s ease !important;
text-decoration: none !important;
color: white !important;
font-size: 0.95rem !important;
font-weight: 600 !important;
min-height: 48px !important;
box-shadow: 0 3px 8px rgba(0,0,0,0.15) !important;
border: none !important;
position: relative !important;
overflow: hidden !important;
}
.vps-control-btn:hover {
transform: translateY(-3px) !important;
box-shadow: 0 6px 16px rgba(0,0,0,0.2) !important;
text-decoration: none !important;
color: white !important;
}
.vps-control-btn:active {
transform: translateY(-1px) !important;
box-shadow: 0 3px 8px rgba(0,0,0,0.15) !important;
}
.vps-control-btn.start-btn {
background: linear-gradient(45deg, #28a745, #34ce57) !important;
color: white !important;
}
.vps-control-btn.start-btn:hover {
background: linear-gradient(45deg, #218838, #28a745) !important;
color: white !important;
}
.vps-control-btn.stop-btn {
background: linear-gradient(45deg, #dc3545, #ff4757) !important;
color: white !important;
}
.vps-control-btn.stop-btn:hover {
background: linear-gradient(45deg, #c82333, #dc3545) !important;
color: white !important;
}
.vps-control-btn.restart-btn {
background: linear-gradient(45deg, #fd7e14, #ff9f43) !important;
color: white !important;
}
.vps-control-btn.restart-btn:hover {
background: linear-gradient(45deg, #e65100, #fd7e14) !important;
color: white !important;
}
.vps-control-btn.vnc-btn {
background: linear-gradient(45deg, #6f42c1, #8854d0) !important;
color: white !important;
}
.vps-control-btn.vnc-btn:hover {
background: linear-gradient(45deg, #5a32a3, #6f42c1) !important;
color: white !important;
}
.vps-control-btn.vnc-btn img {
width: 20px !important;
height: 20px !important;
margin-right: 8px !important;
filter: brightness(0) invert(1);
}
.vps-control-btn i {
margin-right: 8px !important;
font-size: 1.1rem !important;
}
/* Mobile Responsive - Stack VPS Control Buttons Vertically */
@media (max-width: 767px) {
.vps-control-btn {
display: block !important;
width: 100% !important;
margin: 8px 0 !important;
text-align: center !important;
}
}
/* Responsive padding */
@media (max-width: 768px) {
.card-body {
padding: 15px !important;
}
.btn-lg {
padding: 10px 24px !important;
font-size: 14px !important;
}
.resource-box {
padding: 15px 5px;
}
}
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
{**
* VPSAG WHMCS Server Provisioning version 1.1
* VPSAG WHMCS Server Provisioning version 1.6
*
* @package WHMCS
* @copyright ArkHost
@@ -23,7 +23,7 @@
<body class="text-center">
<div class="alert alert-warning" role="alert">
<div class="notice">
<img src="{$image}" class="mr-2">{$error}
<img src="{$image}" class="mr-2">{$error|escape}
</div>
</div>
</body>
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB