getConnection(); function jsonResponse(bool $success, string $message, array $extra = []): void { echo json_encode(array_merge([ 'success' => $success, 'message' => $message ], $extra)); exit; } function getCyberpanelEncryptionKey(): string { return hash('sha256', 'CHANGE_THIS_SECRET_KEY_FOR_CYBERPANEL_DASHBOARD'); } function decryptCyberpanelPassword(string $encryptedPassword): string { if ($encryptedPassword === '') { return ''; } $key = getCyberpanelEncryptionKey(); $decoded = base64_decode($encryptedPassword); if (!$decoded || strpos($decoded, '::') === false) { return ''; } [$ivBase64, $encrypted] = explode('::', $decoded, 2); $iv = base64_decode($ivBase64); if (!$iv) { return ''; } $decrypted = openssl_decrypt( $encrypted, 'AES-256-CBC', $key, 0, $iv ); return $decrypted ?: ''; } function readCookieFromJar(string $cookieFile, string $cookieName): string { if (!file_exists($cookieFile)) { return ''; } $lines = file($cookieFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); foreach ($lines as $line) { if (strpos($line, '#') === 0) { continue; } $parts = preg_split('/\s+/', $line); if (count($parts) >= 7 && $parts[5] === $cookieName) { return $parts[6]; } } return ''; } function cyberpanelGet(string $url, string $cookieFile): array { $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => false, CURLOPT_COOKIEJAR => $cookieFile, CURLOPT_COOKIEFILE => $cookieFile, CURLOPT_TIMEOUT => 30, CURLOPT_CONNECTTIMEOUT => 15, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_USERAGENT => 'Mozilla/5.0 CyberPanelDashboard' ]); $response = curl_exec($ch); $curlError = curl_error($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return [ 'http_code' => $httpCode, 'curl_error' => $curlError, 'raw' => $response ]; } function cyberpanelPostJson(string $url, array $payload, string $cookieFile, string $csrfToken, string $referer): array { $headers = [ 'Content-Type: application/json', 'Accept: application/json', 'X-Csrftoken: ' . $csrfToken, 'Referer: ' . $referer ]; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($payload), CURLOPT_HTTPHEADER => $headers, CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => false, CURLOPT_COOKIEJAR => $cookieFile, CURLOPT_COOKIEFILE => $cookieFile, CURLOPT_TIMEOUT => 30, CURLOPT_CONNECTTIMEOUT => 15, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_USERAGENT => 'Mozilla/5.0 CyberPanelDashboard' ]); $response = curl_exec($ch); $curlError = curl_error($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $json = json_decode((string)$response, true); return [ 'http_code' => $httpCode, 'curl_error' => $curlError, 'raw' => $response, 'json' => is_array($json) ? $json : null ]; } function normalizeWebsiteItems(array $fetchJson): array { $items = []; if (isset($fetchJson['data'])) { if (is_string($fetchJson['data'])) { $decodedData = json_decode($fetchJson['data'], true); if (is_array($decodedData)) { $items = $decodedData; } } elseif (is_array($fetchJson['data'])) { $items = $fetchJson['data']; } } elseif (isset($fetchJson['websites']) && is_array($fetchJson['websites'])) { $items = $fetchJson['websites']; } elseif (isset($fetchJson['records']) && is_array($fetchJson['records'])) { $items = $fetchJson['records']; } $websites = []; foreach ($items as $item) { if (!is_array($item)) { continue; } $domain = $item['domain'] ?? $item['domainName'] ?? $item['website'] ?? $item['name'] ?? ''; $owner = $item['admin'] ?? $item['owner'] ?? $item['user'] ?? $item['websiteOwner'] ?? ''; $ownerUsername = $item['owner_username'] ?? $item['ownerUsername'] ?? $item['username'] ?? $owner ?? ''; $package = $item['package'] ?? $item['packageName'] ?? ''; $phpVersion = $item['php_version'] ?? $item['phpVersion'] ?? $item['php'] ?? ''; $sslStatus = $item['ssl']['status'] ?? $item['ssl_status'] ?? $item['sslStatus'] ?? 'unknown'; $diskUsage = $item['diskUsed'] ?? $item['disk_usage'] ?? $item['diskUsage'] ?? ''; $status = $item['status'] ?? $item['state'] ?? 'unknown'; if ($domain === '') { foreach ($item as $value) { if (is_string($value) && preg_match('/^[a-z0-9.-]+\.[a-z]{2,}$/i', trim($value))) { $domain = trim($value); break; } } } if ($domain !== '') { $websites[] = [ 'domain' => $domain, 'owner' => $owner, 'owner_username' => $ownerUsername, 'package' => $package, 'php_version' => $phpVersion, 'ssl_status' => $sslStatus, 'disk_usage' => $diskUsage, 'status' => strtolower(trim((string)$status)), 'php_version' => trim((string)$phpVersion), 'ssl_status' => strtolower(trim((string)$sslStatus)), 'disk_usage' => trim((string)$diskUsage), 'raw' => $item ]; } } return $websites; } try { $serverId = isset($_POST['server_id']) ? (int)$_POST['server_id'] : 0; if ($serverId <= 0) { jsonResponse(false, 'Missing server ID.'); } $stmt = $db->prepare(" SELECT id, panel_url, username, password_encrypted, api_enabled FROM cyberpanel_servers WHERE id = :id LIMIT 1 "); $stmt->execute([ ':id' => $serverId ]); $server = $stmt->fetch(PDO::FETCH_ASSOC); if (!$server) { jsonResponse(false, 'Server not found.'); } if ((int)$server['api_enabled'] !== 1) { jsonResponse(false, 'API disabled for this server.'); } $panelUrl = rtrim($server['panel_url'], '/'); $username = $server['username']; $password = decryptCyberpanelPassword($server['password_encrypted']); if ($password === '') { jsonResponse(false, 'Password cannot be decrypted.'); } $cookieFile = tempnam(sys_get_temp_dir(), 'cyberpanel_cookie_'); /* * Step 1: open CyberPanel first to obtain csrftoken cookie. */ $initialGet = cyberpanelGet($panelUrl . '/', $cookieFile); if ($initialGet['curl_error'] !== '') { @unlink($cookieFile); jsonResponse(false, 'CyberPanel initial GET failed: ' . $initialGet['curl_error']); } $csrfToken = readCookieFromJar($cookieFile, 'csrftoken'); if ($csrfToken === '') { @unlink($cookieFile); jsonResponse(false, 'CSRF token not found from CyberPanel.', [ 'http_code' => $initialGet['http_code'], 'raw_preview' => substr(strip_tags((string)$initialGet['raw']), 0, 1000) ]); } /* * Step 2: login with csrf token. */ $login = cyberpanelPostJson( $panelUrl . '/verifyLogin', [ 'username' => $username, 'password' => $password, 'languageSelection' => 'english' ], $cookieFile, $csrfToken, $panelUrl . '/' ); if ($login['curl_error'] !== '') { @unlink($cookieFile); jsonResponse(false, 'CyberPanel login cURL error: ' . $login['curl_error']); } if (!$login['json']) { @unlink($cookieFile); jsonResponse(false, 'CyberPanel login did not return JSON.', [ 'http_code' => $login['http_code'], 'raw_preview' => substr(strip_tags((string)$login['raw']), 0, 1000) ]); } $loginOk = false; if (isset($login['json']['loginStatus']) && $login['json']['loginStatus']) { $loginOk = true; } if (isset($login['json']['status']) && (int)$login['json']['status'] === 1) { $loginOk = true; } if (!$loginOk) { @unlink($cookieFile); jsonResponse(false, 'CyberPanel login failed.', [ 'login_response' => $login['json'] ]); } /* * Step 3: after login, read sessionid and fetch websites. */ $csrfTokenAfterLogin = readCookieFromJar($cookieFile, 'csrftoken'); if ($csrfTokenAfterLogin !== '') { $csrfToken = $csrfTokenAfterLogin; } $fetch = cyberpanelPostJson( $panelUrl . '/websites/fetchWebsitesList', [ 'page' => 1, 'recordsToShow' => 1000 ], $cookieFile, $csrfToken, $panelUrl . '/websites/listWebsites' ); @unlink($cookieFile); if ($fetch['curl_error'] !== '') { jsonResponse(false, 'fetchWebsitesList cURL error: ' . $fetch['curl_error']); } if (!$fetch['json']) { jsonResponse(false, 'fetchWebsitesList did not return JSON.', [ 'http_code' => $fetch['http_code'], 'raw_preview' => substr(strip_tags((string)$fetch['raw']), 0, 1000) ]); } if ( isset($fetch['json']['error_message']) && stripos($fetch['json']['error_message'], 'session') !== false ) { jsonResponse(false, 'CyberPanel session not accepted.', [ 'fetch_response' => $fetch['json'] ]); } $websites = normalizeWebsiteItems($fetch['json']); $upsert = $db->prepare(" INSERT INTO cyberpanel_websites ( server_id, domain, website_owner, owner_username, package_name, php_version, ssl_status, website_status, disk_usage, raw_json, last_sync ) VALUES ( :server_id, :domain, :website_owner, :owner_username, :package_name, :php_version, :ssl_status, :website_status, :disk_usage, :raw_json, NOW() ) ON DUPLICATE KEY UPDATE website_owner = VALUES(website_owner), owner_username = VALUES(owner_username), package_name = VALUES(package_name), php_version = VALUES(php_version), ssl_status = VALUES(ssl_status), website_status = VALUES(website_status), disk_usage = VALUES(disk_usage), raw_json = VALUES(raw_json), last_sync = NOW() "); $inserted = 0; $inserted = 0; foreach ($websites as $website) { $upsert->execute([ ':server_id' => $serverId, ':domain' => $website['domain'], ':website_owner' => $website['owner'], ':owner_username' => $website['owner_username'] ?: null, ':package_name' => $website['package'] ?: null, ':php_version' => $website['php_version'] ?: null, ':ssl_status' => $website['ssl_status'] ?: 'unknown', ':website_status' => $website['status'] ?: 'unknown', ':disk_usage' => $website['disk_usage'] ?: null, ':raw_json' => json_encode($website['raw'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ]); $inserted++; } $update = $db->prepare(" UPDATE cyberpanel_servers SET last_status = 'online', last_check = NOW() WHERE id = :server_id "); $update->execute([ ':server_id' => $serverId ]); jsonResponse(true, 'Websites synced successfully.', [ 'count' => $inserted, 'websites' => $websites, 'raw_response' => $fetch['json'] ]); } catch (Throwable $e) { jsonResponse(false, 'Sync websites error: ' . $e->getMessage()); }