Files
2025-10-12 18:37:36 -03:00

591 lines
24 KiB
PHP

<?php
if (!defined("WHMCS")) {
die("This file cannot be accessed directly");
}
use Illuminate\Database\Capsule\Manager as Capsule;
function wgdashboard_MetaData(){
return array(
'DisplayName' => '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() . "<br />" . $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';
$deleteResult = wgdashboard_API($params, 'deletePeers/' . urlencode($configName), [
'peers' => [$publicKey]
], 'POST');
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';
$suspendResult = wgdashboard_API($params, 'restrictPeers/' . urlencode($configName), [
'peers' => [$publicKey]
], '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';
$unsuspendResult = wgdashboard_API($params, 'allowAccessPeers/' . urlencode($configName), [
'peers' => [$publicKey]
], '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, 'updatePeerSettings/' . 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(),
],
];
}
}