From cbf771ab23331a4dea51dc5374a25ca3cf4c1f03 Mon Sep 17 00:00:00 2001 From: iTakinn Date: Sun, 12 Oct 2025 17:49:27 -0300 Subject: [PATCH] made it all --- README.md | 192 ++++++++++++ WGDashboard/clientarea.tpl | 369 +++++++++++++++++++++++ WGDashboard/wgdashboard.php | 584 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1145 insertions(+) create mode 100644 README.md create mode 100644 WGDashboard/clientarea.tpl create mode 100644 WGDashboard/wgdashboard.php diff --git a/README.md b/README.md new file mode 100644 index 0000000..d927339 --- /dev/null +++ b/README.md @@ -0,0 +1,192 @@ +# WGDashboard WHMCS Module + +WHMCS module for integration with [WGDashboard](https://github.com/donaldzou/WGDashboard), enabling automated WireGuard peer management through WHMCS. + +## 📋 Requirements + +- WHMCS 7.0 or higher +- WGDashboard v4.0+ with API enabled +- PHP 7.4 or higher +- PHP cURL extension enabled + +## 🚀 Installation + +1. Upload the module files to the WHMCS directory: + ``` + /modules/servers/wgdashboard/ + ``` + +2. File structure: + ``` + modules/server/wgdashboard/ + ├── wgdashboard.php + └── clientarea.tpl + ``` + +3. In the WHMCS admin panel, navigate to: + **Setup → Products/Services → Servers → Add New Server** + +4. Configure the server: + - **Hostname**: IP or domain of WGDashboard (e.g., `10.0.238.222` or `vpn.example.com`) + - **Module**: WGDashboard + - **Port**: API port (e.g., `10086`) + - **Password**: WGDashboard API Key + +5. Click **Test Connection** to validate the configuration. + +## ⚙️ Product Configuration + +1. Create or edit a product in **Setup → Products/Services** + +2. In the **Module Settings** tab: + - **Module Name**: WGDashboard + - **Server Group**: Select the configured server + +3. **Configuration Options**: + + - **Allowed IPs**: Allowed IPs for the peer (default: `0.0.0.0/0`) + - **Config Name**: WireGuard configuration name in the dashboard (default: `wg0`) + - **Peer Template**: (Optional) JSON template to customize peer creation + +### Peer Template Example + +```json +{ + "DNS": "8.8.8.8,1.1.1.1", + "mtu": 1420, + "keepalive": 25 +} +``` + +Available placeholders: +- `{{client_email}}` - Client's email +- `{{domain}}` - Service domain +- `{{serviceid}}` - Service ID in WHMCS + +## 🔧 Features + +### Peer Automation + +Module options: + +- ✅ **Creation**: Automatically generates keys and creates peer in WGDashboard +- ✅ **Suspension**: Restricts peer access when service is suspended +- ✅ **Reactivation**: Restores access when service is reactivated +- ✅ **Termination**: Completely removes peer when service is cancelled + +### Client Area + +Client Area: + +- 📱 **QR Code**: For quick configuration on mobile devices +- 📄 **Formatted Configuration**: Ready-to-use WireGuard configuration file +- 📋 **Download**: Button to download `.conf` file + + +### Admin Panel + +In the client's service tab, the administrator views: + +- **Public Key**: Peer's public key +- **Peer Name**: Peer identifier name +- **Allowed IPs**: Allowed IPs +- **Status**: Current peer state +- **Latest Handshake**: Last handshake with server +- **Data Received**: Volume of data received From peer to server +- **Data Sent**: Volume of data sent From server to peer + + +## 🌐 WGDashboard Configuration + +To use this module, ensure the WGDashboard API is enabled: + +1. Access WGDashboard settings +2. Navigate to **Settings → API** +3. Enable the API and generate a key +4. Use this key in the **Password** field of the server configuration in WHMCS + +## 🐛 Troubleshooting + +### Connection Test Fails + +**Error 401/403**: Verify API Key +``` +Solution: Confirm that the API Key is correct in the server's Password field +``` + +**Error 404**: Endpoint not found +``` +Solution: Check if your WGDashboard version supports the API +``` + +**Error 500**: WGDashboard internal error +``` +Solution: Check WGDashboard logs on the server +``` + +### Peer Not Created + +1. Enable **Module Debug Log** in WHMCS +2. Navigate to **Utilities → Logs → Module Log** +3. Look for "WGDashboard-WHMCS" entries +4. Analyze request and response details + +### QR Code Doesn't Appear + +Check: +- If JavaScript is enabled in the browser +- Browser console for errors (F12) +- If QRCode.js library is loading correctly + +### Port Not Recognized + +The module uses WHMCS's **Server Port** field to define the API port. +Do not include the port in the hostname, only in the dedicated field. + +✅ **Correct**: +- Hostname: `10.0.0.55` +- Port: `10086` + +❌ **Incorrect**: +- Hostname: `10.0.0.55:10086` +- Port: (empty) + + +## 🔄 Compatibility + +- **WHMCS**: 7.0+ +- **WGDashboard**: v4.0+ +- **PHP**: 7.4, 8.0, 8.1, 8.2 +- **WireGuard**: All versions + +## 📄 License + +This module is provided "as is", without warranties of any kind. + +## 🤝 Contributing + +Contributions are welcome! To contribute: + +1. Fork the repository +2. Create a branch for your feature +3. Commit your changes +4. Push to the branch +5. Open a Pull Request + +## 🔗 Useful Links + +- [WGDashboard GitHub](https://github.com/donaldzou/WGDashboard) +- [WHMCS Developer Documentation](https://developers.whmcs.com/) +- [WireGuard Official Site](https://www.wireguard.com/) + +## 📋 Changelog + +### v1.0.0 +- ✨ Initial release +- ✅ Peer creation, suspension, reactivation, and termination +- 📱 Client area with QR code and formatted configuration +- 🌐 Portuguese translation +- 🔐 Local WireGuard key generation +- 📊 Admin panel with peer statistics + +--- \ No newline at end of file diff --git a/WGDashboard/clientarea.tpl b/WGDashboard/clientarea.tpl new file mode 100644 index 0000000..31a8a17 --- /dev/null +++ b/WGDashboard/clientarea.tpl @@ -0,0 +1,369 @@ +{if $error} +
+ Erro: {$error} +
+{else} +
+
+ {if $formatted_config} +
+
+

+ Código QR +

+
+
+
+

Escaneie este código QR com seu cliente WireGuard

+
+ +
+
+
+ {else} +
+
+

+ Código QR +

+
+
+

Configuração não disponível

+
+
+ {/if} +
+ +
+
+
+

+ Configuração WireGuard +

+
+
+ {if $formatted_config} +
+ +
{$formatted_config|escape}
+
+
+ + +
+ {else} +

Configuração não disponível

+ {/if} +
+
+
+
+{/if} + +{* Modal de Instruções *} + + + + + + \ No newline at end of file diff --git a/WGDashboard/wgdashboard.php b/WGDashboard/wgdashboard.php new file mode 100644 index 0000000..3a2bd65 --- /dev/null +++ b/WGDashboard/wgdashboard.php @@ -0,0 +1,584 @@ + 'WGDashboard', + 'DefaultNonSSLPort' => '10086', + 'DefaultSSLPort' => '443', + 'APIVersion' => '1.0', + 'RequiresServer' => true, + ); +} + +function wgdashboard_GetHostname(array $params) { + $hostname = $params['serverhostname'] ?? ''; + if ($hostname === '') { + throw new Exception('Could not find the WGDashboard\'s hostname - did you configure server group for the product?'); + } + + // WHMCS sometimes stores placeholders like DOT/DASH + foreach ([ 'DOT' => '.', 'DASH' => '-' ] as $from => $to) { + $hostname = str_replace($from, $to, $hostname); + } + + $hostname = preg_replace('#^https?://#i', '', trim($hostname)); + $hostname = rtrim($hostname, '/'); + + // Strip port from hostname; always use $params['serverport'] + if (strpos($hostname, ':') !== false) { + $hostname = explode(':', $hostname)[0]; + } + + $protocol = (!empty($params['serversecure'])) ? 'https://' : 'http://'; + $port = isset($params['serverport']) && is_numeric($params['serverport']) && (int)$params['serverport'] > 0 + ? (int)$params['serverport'] + : null; + + return $protocol . $hostname . ($port ? (':' . $port) : ''); +} + +function wgdashboard_API(array $params, $endpoint, array $data = [], $method = "GET", $dontLog = false) { + $hostname = wgdashboard_GetHostname($params); + $url = $hostname . '/api/' . $endpoint; + + + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method); + curl_setopt($curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + curl_setopt($curl, CURLOPT_USERAGENT, "WGDashboard-WHMCS"); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_301); + curl_setopt($curl, CURLOPT_TIMEOUT, 30); + + $headers = [ + "wg-dashboard-apikey: " . $params['serverpassword'], + "Accept: application/json", + "Content-Type: application/json", + ]; + + if($method === 'POST' || $method === 'PATCH' || $method === 'PUT') { + if(!empty($data)) { + $jsonData = json_encode($data); + curl_setopt($curl, CURLOPT_POSTFIELDS, $jsonData); + $headers[] = "Content-Length: " . strlen($jsonData); + } + } + + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + + $response = curl_exec($curl); + $responseData = json_decode($response, true); + $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + $curlErr = curl_error($curl); + + if($response === false && !$dontLog) logModuleCall("WGDashboard-WHMCS", "CURL ERROR", $curlErr, ""); + + curl_close($curl); + + $result = [ + 'status_code' => $httpCode, + 'data' => $responseData + ]; + + if(!$dontLog) logModuleCall("WGDashboard-WHMCS", $method . " - " . $url, + isset($data) ? json_encode($data) : "", + print_r($result, true)); + + return $result; +} + +function wgdashboard_Error($func, $params, Exception $err) { + logModuleCall("WGDashboard-WHMCS", $func, $params, $err->getMessage(), $err->getTraceAsString()); +} + + +function wgdashboard_ConfigOptions() +{ + return [ + 'Allowed IPs' => [ + 'Type' => 'text', + 'Size' => '40', + 'Description' => 'Default allowed IPs for created peers (e.g. 10.10.0.0/24 or 0.0.0.0/0)', + 'Default' => '0.0.0.0/0', + ], + 'Config Name' => [ + 'Type' => 'text', + 'Size' => '30', + 'Description' => 'WireGuard config name on WGDashboard (if you manage multiple configs)', + 'Default' => 'wg0', + ], + 'Peer Template' => [ + 'Type' => 'textarea', + 'Description' => "Optional JSON template to merge into peer creation payload. Use {{client_email}} or {{domain}} placeholders.", + ], + ]; +} + +function wgdashboard_TestConnection(array $params) { + $solutions = [ + 0 => "Check module debug log for more detailed error.", + 401 => "Authorization header either missing or not provided.", + 403 => "Double check the API key (which should be in the server password field).", + 404 => "Result not found.", + 422 => "Validation error.", + 500 => "WGDashboard errored, check panel logs.", + ]; + + $err = ""; + try { + $response = wgdashboard_API($params, 'getWireguardConfigurations'); + + if($response['status_code'] !== 200) { + $status_code = $response['status_code']; + $err = "Invalid status_code received: " . $status_code . ". Possible solutions: " + . (isset($solutions[$status_code]) ? $solutions[$status_code] : "None."); + } + } catch(Exception $e) { + wgdashboard_Error(__FUNCTION__, $params, $e); + $err = $e->getMessage(); + } + + return [ + "success" => $err === "", + "error" => $err, + ]; +} + +function wgdashboard_GetPeerID(array $params, $raw = false) { + // Try to get from database directly first + if (!empty($params['serviceid'])) { + try { + $service = Capsule::table('tblhosting')->where('id', $params['serviceid'])->first(); + if ($service && !empty($service->username)) { + if ($raw) { + // Get peer info by searching in the configuration + $configName = $params['configoptions']['Config Name'] ?? $params['configoption2'] ?? 'wg0'; + $configResult = wgdashboard_API($params, 'getWireguardConfigurationInfo?configurationName=' . urlencode($configName), [], 'GET', false); // Force logging + + // Debug: Force logging for this call even with dontLog=true + if(function_exists('logModuleCall')) { + logModuleCall("WGDashboard-WHMCS", "GetPeerID RAW Debug", "Config: " . $configName . ", Looking for: " . $service->username, "Status: " . $configResult['status_code'] . "\nResponse: " . print_r($configResult, true)); + } + + if($configResult['status_code'] === 200 && isset($configResult['data']['data']['configurationPeers'])) { + foreach($configResult['data']['data']['configurationPeers'] as $peer) { + // Check both 'id' and 'public_key' fields + if(($peer['id'] ?? '') === $service->username || ($peer['public_key'] ?? '') === $service->username) { + return ['status_code' => 200, 'data' => $peer]; + } + } + + // Debug: If not found, log what peers exist + if(function_exists('logModuleCall')) { + $peerIds = array_map(function($p) { return $p['id'] ?? $p['public_key'] ?? 'no-id'; }, $configResult['data']['data']['configurationPeers']); + logModuleCall("WGDashboard-WHMCS", "GetPeerID NOT FOUND", "Looking for: " . $service->username, "Available peers: " . implode(', ', $peerIds)); + } + } + } else { + return $service->username; // This is the public key/id + } + } + } catch (Exception $e) { + // Continue to other methods + } + } + + // Try to get peer public key from service username first + if (!empty($params['serviceusername'])) { + if ($raw) { + // Get peer info by searching in the configuration + $configName = $params['configoptions']['Config Name'] ?? $params['configoption2'] ?? 'wg0'; + $configResult = wgdashboard_API($params, 'getWireguardConfigurationInfo?configurationName=' . urlencode($configName), [], 'GET', true); + if($configResult['status_code'] === 200 && isset($configResult['data']['data']['configurationPeers'])) { + foreach($configResult['data']['data']['configurationPeers'] as $peer) { + // Check both 'id' and 'public_key' fields + if(($peer['id'] ?? '') === $params['serviceusername'] || ($peer['public_key'] ?? '') === $params['serviceusername']) { + return ['status_code' => 200, 'data' => $peer]; + } + } + } + } else { + return $params['serviceusername']; // This is the public key/id + } + } + + // Try to get from custom fields + if (!empty($params['customfields']['peer_public_key'])) { + if ($raw) { + $configName = $params['configoptions']['Config Name'] ?? $params['configoption2'] ?? 'wg0'; + $configResult = wgdashboard_API($params, 'getWireguardConfigurationInfo?configurationName=' . urlencode($configName), [], 'GET', true); + if($configResult['status_code'] === 200 && isset($configResult['data']['data']['configurationPeers'])) { + foreach($configResult['data']['data']['configurationPeers'] as $peer) { + // Check both 'id' and 'public_key' fields + if(($peer['id'] ?? '') === $params['customfields']['peer_public_key'] || ($peer['public_key'] ?? '') === $params['customfields']['peer_public_key']) { + return ['status_code' => 200, 'data' => $peer]; + } + } + } + } else { + return $params['customfields']['peer_public_key']; + } + } + + return null; +} + +function wgdashboard_GenerateKeyPair() { + if (function_exists('exec') && !in_array('exec', explode(',', ini_get('disable_functions')))) { + $privateKey = ''; + $publicKey = ''; + + exec('wg genkey 2>/dev/null', $output, $return_code); + if ($return_code === 0 && !empty($output[0])) { + $privateKey = trim($output[0]); + + // Generate public key from private key + $publicOutput = []; + exec("echo '$privateKey' | wg pubkey 2>/dev/null", $publicOutput, $pub_return_code); + if ($pub_return_code === 0 && !empty($publicOutput[0])) { + $publicKey = trim($publicOutput[0]); + + return [ + 'private_key' => $privateKey, + 'public_key' => $publicKey + ]; + } + } + } + + return [ + 'private_key' => '', + 'public_key' => '' + ]; +} + +function wgdashboard_CreateAccount(array $params) +{ + try { + $peerId = wgdashboard_GetPeerID($params); + if(isset($peerId)) throw new Exception('Failed to create peer because it is already created.'); + + $clientEmail = $params['clientsdetails']['email'] ?? ''; + $domain = $params['domain'] ?? ''; + $serviceId = $params['serviceid'] ?? null; + + $allowedIpsDefault = $params['configoptions']['Allowed IPs'] ?? $params['configoption1'] ?? '0.0.0.0/0'; + $configName = $params['configoptions']['Config Name'] ?? $params['configoption2'] ?? 'wg0'; + + $peerName = preg_replace('/[^a-zA-Z0-9_\-\.]/', '_', ($params['clientsdetails']['email'] ?? 'peer') . '_' . ($serviceId ?: uniqid())); + + $keys = wgdashboard_GenerateKeyPair(); + + $payload = [ + "name" => $peerName, + "DNS" => "1.1.1.1", + "endpoint_allowed_ip" => "0.0.0.0/0", + "keepalive" => 21, + "mtu" => 1420, + "preshared_key" => "", + "preshared_key_bulkAdd" => false, + "advanced_security" => "off", + ]; + + // Only add keys if they were successfully generated + if (!empty($keys['private_key']) && !empty($keys['public_key'])) { + $payload["private_key"] = $keys['private_key']; + $payload["public_key"] = $keys['public_key']; + } + + if (!empty($params['configoptions']['Peer Template'])) { + $template = $params['configoptions']['Peer Template']; + $template = str_replace(['{{client_email}}', '{{domain}}', '{{serviceid}}'], [$clientEmail, $domain, $serviceId], $template); + $jsonTemplate = @json_decode($template, true); + if (is_array($jsonTemplate)) { + $payload = array_merge($payload, $jsonTemplate); + } + } + + $result = wgdashboard_API($params, 'addPeers/' . urlencode($configName), $payload, 'POST'); + + if($result['status_code'] !== 200) { + throw new Exception('WGDashboard create failed: HTTP ' . $result['status_code'] . ' - ' . print_r($result['data'], true)); + } + + $publicKeyToStore = ''; + if (!empty($keys['public_key'])) { + $publicKeyToStore = $keys['public_key']; + } else { + if (isset($result['data']['data']) && is_array($result['data']['data']) && !empty($result['data']['data'][0])) { + $createdPeer = $result['data']['data'][0]; + $publicKeyToStore = $createdPeer['id'] ?? ''; + } + + if (empty($publicKeyToStore)) { + sleep(1); + $configResult = wgdashboard_API($params, 'getWireguardConfigurationInfo?configurationName=' . urlencode($configName), [], 'GET', true); + if($configResult['status_code'] === 200 && isset($configResult['data']['data']['configurationPeers'])) { + foreach($configResult['data']['data']['configurationPeers'] as $peer) { + if($peer['name'] === $peerName) { + $publicKeyToStore = $peer['id'] ?? $peer['public_key'] ?? ''; + break; + } + } + } + } + } + + if (empty($publicKeyToStore)) { + throw new Exception('Failed to obtain public key for created peer'); + } + + try { + Capsule::table('tblhosting')->where('id', $params['serviceid'])->update([ + 'username' => $publicKeyToStore, + 'password' => '', + ]); + } catch (Exception $e) { + return $e->getMessage() . "
" . $e->getTraceAsString(); + } + + } catch(Exception $err) { + return $err->getMessage(); + } + + return 'success'; +} + +function wgdashboard_TerminateAccount($params) +{ + try { + $publicKey = wgdashboard_GetPeerID($params); + if(!isset($publicKey)) throw new Exception('Failed to terminate peer because it doesn\'t exist.'); + + $configName = $params['configoptions']['Config Name'] ?? $params['configoption2'] ?? 'wg0'; + $publicKeyEncoded = urlencode($publicKey); + $deleteResult = wgdashboard_API($params, 'deletePeers/' . urlencode($configName) . '?id=' . $publicKeyEncoded, [], 'DELETE'); + + if($deleteResult['status_code'] !== 200) throw new Exception('Failed to delete the peer, received error code: ' . $deleteResult['status_code'] . '. Enable module debug log for more info.'); + + } catch(Exception $err) { + return $err->getMessage(); + } + + return 'success'; +} + +function wgdashboard_SuspendAccount($params) +{ + try { + $publicKey = wgdashboard_GetPeerID($params); + if(!isset($publicKey)) throw new Exception('Failed to suspend peer because it doesn\'t exist.'); + + $configName = $params['configoptions']['Config Name'] ?? $params['configoption2'] ?? 'wg0'; + $publicKeyEncoded = urlencode($publicKey); + $suspendResult = wgdashboard_API($params, 'restrictPeers/' . urlencode($configName) . '?id=' . $publicKeyEncoded, [], 'POST'); + + if($suspendResult['status_code'] !== 200) throw new Exception('Failed to suspend the peer, received error code: ' . $suspendResult['status_code'] . '. Enable module debug log for more info.'); + } catch(Exception $err) { + return $err->getMessage(); + } + + return 'success'; +} + +function wgdashboard_UnsuspendAccount($params) +{ + try { + $publicKey = wgdashboard_GetPeerID($params); + if(!isset($publicKey)) throw new Exception('Failed to unsuspend peer because it doesn\'t exist.'); + + $configName = $params['configoptions']['Config Name'] ?? $params['configoption2'] ?? 'wg0'; + $publicKeyEncoded = urlencode($publicKey); + $unsuspendResult = wgdashboard_API($params, 'allowAccessPeers/' . urlencode($configName) . '?id=' . $publicKeyEncoded, [], 'POST'); + + if($unsuspendResult['status_code'] !== 200) throw new Exception('Failed to unsuspend the peer, received error code: ' . $unsuspendResult['status_code'] . '. Enable module debug log for more info.'); + } catch(Exception $err) { + return $err->getMessage(); + } + + return 'success'; +} + +function wgdashboard_ChangePackage($params) +{ + try { + $peerId = wgdashboard_GetPeerID($params); + if(!isset($peerId)) throw new Exception('Failed to change package because peer doesn\'t exist.'); + + $configName = $params['configoptions']['Config Name'] ?? $params['configoption2'] ?? 'wg0'; + $allowedIps = $params['configoptions']['Allowed IPs'] ?? $params['configoption1'] ?? '0.0.0.0/0'; + + $updateData = [ + 'allowed_ip' => $allowedIps, + ]; + + $updateResult = wgdashboard_API($params, 'updatePeer/' . urlencode($configName) . '/' . urlencode($peerId), $updateData, 'POST'); + + if($updateResult['status_code'] !== 200) throw new Exception('Failed to update the peer, received error code: ' . $updateResult['status_code'] . '. Enable module debug log for more info.'); + } catch(Exception $err) { + return $err->getMessage(); + } + + return 'success'; +} + +function wgdashboard_AdminServicesTabFields($params) +{ + $fields = []; + + try { + $publicKey = wgdashboard_GetPeerID($params); + $fields['Public Key'] = $publicKey ?: 'Not found'; + + if($publicKey) { + $peerData = wgdashboard_GetPeerID($params, true); + + if($peerData && $peerData['status_code'] === 200 && isset($peerData['data'])) { + $peer = $peerData['data']; + + $fields['Peer Name'] = $peer['name'] ?? 'N/A'; + $fields['Allowed IPs'] = $peer['allowed_ip'] ?? 'N/A'; + $fields['Status'] = ucfirst($peer['status'] ?? 'N/A'); + + $latestHandshake = $peer['latest_handshake'] ?? null; + if ($latestHandshake && is_numeric($latestHandshake) && $latestHandshake > 0) { + $fields['Latest Handshake'] = date('Y-m-d H:i:s', $latestHandshake); + } elseif ($latestHandshake && preg_match('/^\d+:\d+:\d+$/', $latestHandshake)) { + $fields['Latest Handshake'] = $latestHandshake . ' ago'; + } else { + $fields['Latest Handshake'] = 'Never'; + } + + $cumuReceive = floatval($peer['cumu_receive'] ?? 0); + $totalReceive = floatval($peer['total_receive'] ?? 0); + $totalReceivedMB = $cumuReceive + $totalReceive; + + if ($totalReceivedMB >= 1) { + $fields['Data Received P->S'] = number_format($totalReceivedMB, 2) . ' GB'; + } else { + $fields['Data Received P->S'] = number_format($totalReceivedMB*1024, 2) . ' MB'; + } + + $cumuSent = floatval($peer['cumu_sent'] ?? 0); + $totalSent = floatval($peer['total_sent'] ?? 0); + $totalSentMB = $cumuSent + $totalSent; + + if ($totalSentMB >= 1) { + $fields['Data Sent S->P'] = number_format($totalSentMB, 2) . ' GB'; + } else { + $fields['Data Sent S->P'] = number_format($totalSentMB*1024, 2) . ' MB'; + } + + $totalDataMB = $totalReceivedMB + $totalSentMB; + if ($totalDataMB >= 1) { + $fields['Total Data'] = number_format($totalDataMB, 2) . ' GB'; + } else { + $fields['Total Data'] = number_format($totalDataMB*1024, 2) . ' MB'; + } + } + } + } catch (Exception $e) { + $fields['Error'] = $e->getMessage(); + } + + return $fields; +} + +function wgdashboard_ClientArea(array $params) { + if($params['moduletype'] !== 'wgdashboard') return; + + try { + $hostname = wgdashboard_GetHostname($params); + $publicKey = wgdashboard_GetPeerID($params); + + if(!$publicKey) { + return [ + 'templatefile' => 'clientarea', + 'vars' => [ + 'error' => 'Peer não encontrado ou ainda não criado.', + ], + ]; + } + + $configName = $params['configoptions']['Config Name'] ?? $params['configoption2'] ?? 'wg0'; + $peerData = wgdashboard_GetPeerID($params, true); + + if(!$peerData || $peerData['status_code'] !== 200) { + return [ + 'templatefile' => 'clientarea', + 'vars' => [ + 'error' => 'Falha ao recuperar dados do peer.', + ], + ]; + } + + $peer = $peerData['data']; + + $formattedConfig = ''; + if (!empty($peer['private_key'])) { + $formattedConfig = "[Interface]\n"; + $formattedConfig .= "PrivateKey = " . ($peer['private_key'] ?? '') . "\n"; + $formattedConfig .= "Address = " . ($peer['allowed_ip'] ?? '10.0.0.1/32') . "\n"; + $formattedConfig .= "MTU = " . ($peer['mtu'] ?? '1420') . "\n"; + $formattedConfig .= "DNS = " . ($peer['DNS'] ?? '1.1.1.1') . "\n"; + $formattedConfig .= "\n"; + $formattedConfig .= "[Peer]\n"; + + $serverPublicKey = ''; + if (isset($peer['configuration']['PublicKey'])) { + $serverPublicKey = $peer['configuration']['PublicKey']; + } else { + $configInfoResult = wgdashboard_API($params, 'getWireguardConfigurationInfo?configurationName=' . urlencode($configName), [], 'GET', true); + if($configInfoResult['status_code'] === 200 && isset($configInfoResult['data']['data']['configurationInfo']['PublicKey'])) { + $serverPublicKey = $configInfoResult['data']['data']['configurationInfo']['PublicKey']; + } + } + + $formattedConfig .= "PublicKey = " . ($serverPublicKey ?: 'N/A') . "\n"; + $formattedConfig .= "AllowedIPs = " . ($peer['endpoint_allowed_ip'] ?? '0.0.0.0/0') . "\n"; + + $endpoint = 'N/A'; + if (!empty($peer['remote_endpoint'])) { + $port = ''; + if (isset($peer['configuration']['ListenPort'])) { + $port = $peer['configuration']['ListenPort']; + } else { + if (isset($configInfoResult) && $configInfoResult['status_code'] === 200 && isset($configInfoResult['data']['data']['configurationInfo']['ListenPort'])) { + $port = $configInfoResult['data']['data']['configurationInfo']['ListenPort']; + } else { + $port = '51820'; + } + } + $endpoint = $peer['remote_endpoint'] . ':' . $port; + } + + $formattedConfig .= "Endpoint = " . $endpoint . "\n"; + $formattedConfig .= "PersistentKeepalive = " . ($peer['keepalive'] ?? '21') . "\n"; + } + + return [ + 'templatefile' => 'clientarea', + 'vars' => [ + 'formatted_config' => $formattedConfig, + 'peer_name' => $peer['name'] ?? 'wireguard', + ], + ]; + + } catch (Exception $err) { + return [ + 'templatefile' => 'clientarea', + 'vars' => [ + 'error' => $err->getMessage(), + ], + ]; + } +}