hostname = rtrim(trim($hostname), '/'); $this->username = trim($username); $this->apiToken = trim($apiToken); $this->port = $port; $this->ssl = $ssl; } /** * Get the base URL for API calls */ private function getBaseUrl(): string { $protocol = $this->ssl ? 'https' : 'http'; return "{$protocol}://{$this->hostname}:{$this->port}"; } /** * Make a UAPI request to cPanel */ public function uapi(string $module, string $function, array $params = []): array { return $this->uapiWithTimeout($module, $function, $params, 30); } /** * Make a UAPI request to cPanel with custom timeout */ public function uapiWithTimeout(string $module, string $function, array $params = [], int $timeout = 30): array { $url = $this->getBaseUrl()."/execute/{$module}/{$function}"; Log::info('cPanel UAPI request', ['url' => $url, 'module' => $module, 'function' => $function, 'timeout' => $timeout]); try { $response = Http::withHeaders([ 'Authorization' => "cpanel {$this->username}:{$this->apiToken}", ]) ->timeout($timeout) ->connectTimeout(10) ->withoutVerifying() ->get($url, $params); Log::info('cPanel UAPI response status', ['status' => $response->status(), 'module' => $module]); if (! $response->successful()) { throw new Exception('API request failed with status: '.$response->status()); } $data = $response->json(); Log::info('cPanel UAPI response data', ['module' => $module, 'function' => $function, 'data' => $data]); if (isset($data['status']) && $data['status'] === 0) { throw new Exception($data['errors'][0] ?? 'Unknown API error'); } return $data; } catch (Exception $e) { Log::error('cPanel API error: '.$e->getMessage(), [ 'module' => $module, 'function' => $function, ]); throw $e; } } /** * Make an API2 request to cPanel (legacy API) */ public function api2(string $module, string $function, array $params = []): array { $url = $this->getBaseUrl().'/json-api/cpanel'; $queryParams = array_merge([ 'cpanel_jsonapi_user' => $this->username, 'cpanel_jsonapi_apiversion' => '2', 'cpanel_jsonapi_module' => $module, 'cpanel_jsonapi_func' => $function, ], $params); try { $response = Http::withHeaders([ 'Authorization' => "cpanel {$this->username}:{$this->apiToken}", ]) ->timeout(120) ->withoutVerifying() ->get($url, $queryParams); if (! $response->successful()) { throw new Exception('API request failed with status: '.$response->status()); } return $response->json(); } catch (Exception $e) { Log::error('cPanel API2 error: '.$e->getMessage(), [ 'module' => $module, 'function' => $function, ]); throw $e; } } /** * Test the connection to cPanel */ public function testConnection(): array { try { $result = $this->uapi('ResourceUsage', 'get_usages'); return [ 'success' => true, 'message' => 'Connection successful', 'data' => $result['result']['data'] ?? [], ]; } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * Get account information */ public function getAccountInfo(): array { try { $stats = $this->uapi('StatsBar', 'get_stats', [ 'display' => 'diskusage|bandwidthusage|addondomains|subdomains|parkeddomains|sqldatabases|emailaccounts', ]); return [ 'success' => true, 'stats' => $stats['result']['data'] ?? [], ]; } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * List all domains (main, addon, subdomains, parked) */ public function listDomains(): array { try { $result = $this->uapi('DomainInfo', 'list_domains'); // Log raw response for debugging Log::info('cPanel listDomains raw response', ['result' => $result]); // Handle different response structures $data = $result['result']['data'] ?? $result['data'] ?? $result; return [ 'success' => true, 'main_domain' => $data['main_domain'] ?? '', 'addon_domains' => $data['addon_domains'] ?? [], 'sub_domains' => $data['sub_domains'] ?? [], 'parked_domains' => $data['parked_domains'] ?? [], ]; } catch (Exception $e) { Log::error('cPanel listDomains failed', ['error' => $e->getMessage()]); return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * List MySQL databases */ public function listDatabases(): array { try { $result = $this->uapi('Mysql', 'list_databases'); // Handle different response structures $data = $result['result']['data'] ?? $result['data'] ?? []; return [ 'success' => true, 'databases' => $data, ]; } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * List MySQL users */ public function listDatabaseUsers(): array { try { $result = $this->uapi('Mysql', 'list_users'); $data = $result['result']['data'] ?? $result['data'] ?? []; return [ 'success' => true, 'users' => $data, ]; } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * List email accounts */ public function listEmailAccounts(): array { try { $result = $this->uapi('Email', 'list_pops_with_disk'); $data = $result['result']['data'] ?? $result['data'] ?? []; return [ 'success' => true, 'accounts' => $data, ]; } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * List email forwarders */ public function listForwarders(): array { try { $result = $this->uapi('Email', 'list_forwarders'); $data = $result['result']['data'] ?? $result['data'] ?? []; return [ 'success' => true, 'forwarders' => $data, ]; } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * List SSL certificates */ public function listSslCertificates(): array { try { $result = $this->uapi('SSL', 'list_certs'); $data = $result['result']['data'] ?? $result['data'] ?? []; return [ 'success' => true, 'certificates' => $data, ]; } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * List cron jobs */ public function listCronJobs(): array { try { $result = $this->uapi('Cron', 'list_cron'); $data = $result['result']['data'] ?? $result['data'] ?? []; return [ 'success' => true, 'cron_jobs' => $data, ]; } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * Create a full backup to homedir */ public function createBackup(): array { try { $result = $this->uapi('Backup', 'fullbackup_to_homedir'); return [ 'success' => true, 'message' => 'Backup initiated', 'pid' => $result['result']['data']['pid'] ?? null, 'data' => $result['result']['data'] ?? [], ]; } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * List available backups in homedir using API2 (more reliable) */ public function listBackups(): array { try { // Use API2 Backups/listfullbackups as it's more reliable $result = $this->api2('Backups', 'listfullbackups', []); $backups = $result['cpanelresult']['data'] ?? []; // Format the backups consistently $formattedBackups = []; foreach ($backups as $backup) { $formattedBackups[] = [ 'file' => $backup['file'] ?? '', 'status' => $backup['status'] ?? 'unknown', 'time' => $backup['time'] ?? 0, 'localtime' => $backup['localtime'] ?? '', 'path' => "/home/{$this->username}/".$backup['file'], ]; } return [ 'success' => true, 'backups' => $formattedBackups, ]; } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * Get the cPanel File Manager URL for downloading a backup */ public function getBackupDownloadUrl(string $filename): string { $protocol = $this->ssl ? 'https' : 'http'; return "{$protocol}://{$this->hostname}:{$this->port}/cpsess0/frontend/jupiter/filemanager/index.html"; } /** * Get direct instructions for downloading a backup */ public function getDownloadInstructions(string $filename): array { $protocol = $this->ssl ? 'https' : 'http'; $loginUrl = "{$protocol}://{$this->hostname}:{$this->port}"; return [ 'login_url' => $loginUrl, 'username' => $this->username, 'steps' => [ "1. Log in to cPanel at {$loginUrl}", '2. Go to File Manager', "3. Navigate to the home directory (/home/{$this->username})", "4. Right-click on '{$filename}' and select 'Download'", '5. Once downloaded, upload the file using the form below', ], 'backup_path' => "/home/{$this->username}/{$filename}", ]; } /** * Check if a backup is currently in progress */ public function getBackupStatus(): array { try { // Use API2 Fileman/listfiles as UAPI list_files returns empty on some cPanel versions $result = $this->api2('Fileman', 'listfiles', [ 'dir' => "/home/{$this->username}", 'showdotfiles' => 0, ]); $files = $result['cpanelresult']['data'] ?? []; $backupFiles = []; $inProgress = false; foreach ($files as $file) { $name = $file['file'] ?? $file['name'] ?? ''; // cPanel backup files are named like: backup-MM.DD.YYYY_HH-mm-ss_username.tar.gz if (preg_match('/^backup-\d+\.\d+\.\d+_\d+-\d+-\d+_.*\.tar\.gz$/', $name)) { $backupFiles[] = [ 'name' => $name, 'size' => $file['size'] ?? 0, 'mtime' => $file['mtime'] ?? 0, 'path' => "/home/{$this->username}/{$name}", ]; } // Check for in-progress backup indicator if (str_contains($name, 'backup') && str_ends_with($name, '.log')) { $inProgress = true; } } // Sort by modification time, newest first usort($backupFiles, fn ($a, $b) => ($b['mtime'] ?? 0) - ($a['mtime'] ?? 0)); return [ 'success' => true, 'in_progress' => $inProgress, 'backups' => $backupFiles, ]; } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * List files in a directory */ public function listFiles(string $dir = '/'): array { try { $result = $this->uapi('Fileman', 'list_files', [ 'dir' => $dir, 'include_mime' => 0, 'include_permissions' => 1, 'include_hash' => 0, 'include_content' => 0, ]); return [ 'success' => true, 'files' => $result['result']['data'] ?? [], ]; } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * Download a file from cPanel to a local path (streaming) */ public function downloadFileToPath(string $remotePath, string $localPath, ?callable $progressCallback = null): array { $url = $this->getBaseUrl().'/download?file='.urlencode($remotePath); Log::info('cPanel download starting', ['remote' => $remotePath, 'local' => $localPath]); try { // First, get the file size $files = $this->listFiles(dirname($remotePath)); $fileSize = 0; if ($files['success']) { foreach ($files['files'] as $file) { if (($file['file'] ?? $file['name'] ?? '') === basename($remotePath)) { $fileSize = (int) ($file['size'] ?? 0); break; } } } // Create a stream context for downloading $context = stream_context_create([ 'http' => [ 'method' => 'GET', 'header' => "Authorization: cpanel {$this->username}:{$this->apiToken}\r\n", 'timeout' => 3600, // 1 hour timeout for large files 'ignore_errors' => true, ], 'ssl' => [ 'verify_peer' => false, 'verify_peer_name' => false, ], ]); // Open remote file $remoteStream = @fopen($url, 'rb', false, $context); if (! $remoteStream) { throw new Exception("Failed to open remote file: $remotePath"); } // Ensure local directory exists $localDir = dirname($localPath); if (! is_dir($localDir)) { mkdir($localDir, 0755, true); } // Open local file for writing $localStream = fopen($localPath, 'wb'); if (! $localStream) { fclose($remoteStream); throw new Exception("Failed to open local file for writing: $localPath"); } // Download in chunks $downloaded = 0; $chunkSize = 1024 * 1024; // 1MB chunks while (! feof($remoteStream)) { $chunk = fread($remoteStream, $chunkSize); if ($chunk === false) { break; } fwrite($localStream, $chunk); $downloaded += strlen($chunk); if ($progressCallback && $fileSize > 0) { $progressCallback($downloaded, $fileSize); } } fclose($remoteStream); fclose($localStream); // Verify file was downloaded if (! file_exists($localPath) || filesize($localPath) === 0) { throw new Exception('Download failed - file is empty or missing'); } Log::info('cPanel download completed', [ 'remote' => $remotePath, 'local' => $localPath, 'size' => filesize($localPath), ]); return [ 'success' => true, 'path' => $localPath, 'size' => filesize($localPath), ]; } catch (Exception $e) { Log::error('cPanel download error: '.$e->getMessage(), [ 'remote' => $remotePath, 'local' => $localPath, ]); // Clean up partial download if (file_exists($localPath)) { @unlink($localPath); } return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * Get homedir path */ public function getHomedir(): string { return "/home/{$this->username}"; } /** * Get the cPanel username */ public function getUsername(): string { return $this->username; } /** * Import an SSH public key to cPanel * Uses API2 SSH::importkey function * * @param string $keyName Name for the key in cPanel * @param string $publicKey The SSH public key content */ public function importSshKey(string $keyName, string $publicKey): array { try { Log::info('Importing SSH key to cPanel', ['key_name' => $keyName, 'key_length' => strlen($publicKey)]); // Use API2 SSH::importkey $result = $this->api2('SSH', 'importkey', [ 'key' => $publicKey, 'name' => $keyName, ]); Log::info('cPanel SSH import response', ['result' => $result]); $data = $result['cpanelresult']['data'][0] ?? []; $apiError = $result['cpanelresult']['error'] ?? ''; // Check for "already exists" which is OK (can appear in different places) if (str_contains($apiError, 'already exists') || str_contains($data['reason'] ?? '', 'already exists')) { Log::info('SSH key already exists on cPanel - treating as success'); return [ 'success' => true, 'message' => 'SSH key already exists', ]; } // Check for API-level error if ($apiError) { throw new Exception($apiError); } // Check for success via event.result (cPanel API2 pattern) $eventResult = $result['cpanelresult']['event']['result'] ?? null; if ($eventResult == 1) { return [ 'success' => true, 'message' => 'SSH key imported successfully', ]; } // Legacy check for data[0].result if (isset($data['result']) && $data['result'] == 1) { return [ 'success' => true, 'message' => 'SSH key imported successfully', ]; } // Check for alternative error locations $errorMsg = ($data['reason'] ?? '') ?: ($data['error'] ?? '') ?: ($result['cpanelresult']['event']['reason'] ?? '') ?: 'Failed to import SSH key (unknown reason)'; throw new Exception($errorMsg); } catch (Exception $e) { Log::error('cPanel SSH key import failed: '.$e->getMessage()); return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * Delete an SSH key from cPanel * Uses API2 SSH::delkey function * * @param string $keyName Name of the key to delete * @param string $keyType Type of key: 'key' for private, 'key.pub' for public */ public function deleteSshKey(string $keyName, string $keyType = 'key'): array { try { Log::info('Deleting SSH key from cPanel', ['key_name' => $keyName, 'type' => $keyType]); $result = $this->api2('SSH', 'delkey', [ 'key' => $keyName, 'pub' => $keyType === 'key.pub' ? 1 : 0, ]); Log::info('cPanel SSH delete response', ['result' => $result]); $apiError = $result['cpanelresult']['error'] ?? ''; $eventResult = $result['cpanelresult']['event']['result'] ?? null; // Check for success if ($eventResult == 1 && empty($apiError)) { return [ 'success' => true, 'message' => 'SSH key deleted', ]; } // Key not found is OK if (str_contains($apiError, 'does not exist') || str_contains($apiError, 'not found')) { return [ 'success' => true, 'message' => 'Key does not exist', ]; } if ($apiError) { throw new Exception($apiError); } return [ 'success' => true, 'message' => 'SSH key deleted', ]; } catch (Exception $e) { Log::error('cPanel SSH key delete failed: '.$e->getMessage()); return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * Import an SSH private key to cPanel (for outgoing connections) * Uses API2 SSH::importkey function * * @param string $keyName Name for the key in cPanel * @param string $privateKey The SSH private key content * @param string $passphrase Optional passphrase for the key */ public function importSshPrivateKey(string $keyName, string $privateKey, string $passphrase = ''): array { try { Log::info('Importing SSH private key to cPanel', ['key_name' => $keyName, 'key_length' => strlen($privateKey)]); // Use API2 SSH::importkey - private keys are detected by content $params = [ 'key' => $privateKey, 'name' => $keyName, ]; if (! empty($passphrase)) { $params['pass'] = $passphrase; } $result = $this->api2('SSH', 'importkey', $params); Log::info('cPanel SSH private key import response', ['result' => $result]); $data = $result['cpanelresult']['data'][0] ?? []; $apiError = $result['cpanelresult']['error'] ?? ''; // Check for "already exists" which is OK if (str_contains($apiError, 'already exists') || str_contains($data['reason'] ?? '', 'already exists')) { Log::info('SSH private key already exists on cPanel - treating as success'); return [ 'success' => true, 'message' => 'SSH key already exists', ]; } // Check for API-level error if ($apiError) { throw new Exception($apiError); } // Check for success via event.result (cPanel API2 pattern) $eventResult = $result['cpanelresult']['event']['result'] ?? null; if ($eventResult == 1) { return [ 'success' => true, 'message' => 'SSH private key imported successfully', ]; } // Legacy check for data[0].result if (isset($data['result']) && $data['result'] == 1) { return [ 'success' => true, 'message' => 'SSH private key imported successfully', ]; } // Check for alternative error locations $errorMsg = ($data['reason'] ?? '') ?: ($data['error'] ?? '') ?: ($result['cpanelresult']['event']['reason'] ?? '') ?: 'Failed to import SSH private key (unknown reason)'; throw new Exception($errorMsg); } catch (Exception $e) { Log::error('cPanel SSH private key import failed: '.$e->getMessage()); return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * Authorize an SSH key in cPanel (make it usable) * Uses API2 SSH::authkey function */ public function authorizeSshKey(string $keyName): array { try { Log::info('Authorizing SSH key in cPanel', ['key_name' => $keyName]); $result = $this->api2('SSH', 'authkey', [ 'key' => $keyName, 'action' => 'authorize', ]); Log::info('cPanel SSH authkey response', ['result' => $result]); $data = $result['cpanelresult']['data'][0] ?? []; $apiError = $result['cpanelresult']['error'] ?? ''; // Check for "already authorized" which is OK if (str_contains($apiError, 'already authorized') || str_contains($data['reason'] ?? '', 'already authorized')) { return [ 'success' => true, 'message' => 'SSH key already authorized', ]; } // Check for API-level error first if ($apiError) { throw new Exception($apiError); } // Check for success via event.result (cPanel API2 pattern) $eventResult = $result['cpanelresult']['event']['result'] ?? null; if ($eventResult == 1) { return [ 'success' => true, 'message' => 'SSH key authorized successfully', ]; } // Legacy check for data[0].result if (isset($data['result']) && $data['result'] == 1) { return [ 'success' => true, 'message' => 'SSH key authorized successfully', ]; } $errorMsg = $data['reason'] ?? 'Failed to authorize SSH key'; throw new Exception($errorMsg); } catch (Exception $e) { Log::error('cPanel SSH key authorization failed: '.$e->getMessage()); return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * Create a full backup and upload to remote server via SCP with key authentication * The SSH key must be previously imported to cPanel using importSshKey() * * @param string $remoteHost The remote server hostname/IP * @param string $remoteUser The SSH username on the remote server * @param string $remotePath The destination path on the remote server * @param string $keyName Name of the SSH key stored in cPanel * @param int $remotePort SSH port (default 22) */ public function createBackupToScpWithKey( string $remoteHost, string $remoteUser, string $remotePath, string $keyName, int $remotePort = 22 ): array { try { $params = [ 'host' => $remoteHost, 'username' => $remoteUser, 'directory' => $remotePath, 'key_name' => $keyName, 'port' => $remotePort, 'key_passphrase' => '', // Empty passphrase for keys generated without one ]; Log::info('cPanel backup to SCP with key initiated', [ 'host' => $remoteHost, 'user' => $remoteUser, 'path' => $remotePath, 'key_name' => $keyName, 'port' => $remotePort, ]); // Use longer timeout for backup operations $result = $this->uapiWithTimeout('Backup', 'fullbackup_to_scp_with_key', $params, 120); return [ 'success' => true, 'message' => 'Backup initiated with SCP transfer', 'pid' => $result['result']['data']['pid'] ?? null, 'data' => $result['result']['data'] ?? [], ]; } catch (Exception $e) { Log::error('cPanel backup to SCP with key failed: '.$e->getMessage()); return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * Legacy method - kept for backward compatibility * * @deprecated Use createBackupToScpWithKey instead */ public function createBackupToScp( string $remoteHost, string $remoteUser, string $remotePath, string $privateKey, string $passphrase = '', int $remotePort = 22 ): array { // This method is deprecated - the key should be imported first Log::warning('createBackupToScp is deprecated, use createBackupToScpWithKey instead'); return [ 'success' => false, 'message' => 'This method is deprecated. Import the SSH key first using importSshKey(), then use createBackupToScpWithKey()', ]; } /** * Create a full backup and upload to remote server via SCP with password authentication */ public function createBackupToScpWithPassword( string $remoteHost, string $remoteUser, string $remotePath, string $password, int $remotePort = 22 ): array { try { $params = [ 'host' => $remoteHost, 'user' => $remoteUser, 'directory' => $remotePath, 'password' => $password, 'port' => $remotePort, ]; Log::info('cPanel backup to SCP (password) initiated', [ 'host' => $remoteHost, 'user' => $remoteUser, 'path' => $remotePath, 'port' => $remotePort, ]); // Use longer timeout for backup operations (120 seconds) $result = $this->uapiWithTimeout('Backup', 'fullbackup_to_scp_with_password', $params, 120); return [ 'success' => true, 'message' => 'Backup initiated with SCP transfer', 'pid' => $result['result']['data']['pid'] ?? null, 'data' => $result['result']['data'] ?? [], ]; } catch (Exception $e) { Log::error('cPanel backup to SCP (password) failed: '.$e->getMessage()); return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * Export a database */ public function exportDatabase(string $database): array { try { // Use mysqldump via cPanel's backup API $result = $this->uapi('Mysql', 'dump_database', [ 'dbname' => $database, ]); return [ 'success' => true, 'data' => $result['result']['data'] ?? '', ]; } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * Get SSL certificate for a domain */ public function getSslCertificate(string $domain): array { try { $result = $this->uapi('SSL', 'fetch_best_for_domain', [ 'domain' => $domain, ]); return [ 'success' => true, 'certificate' => $result['result']['data'] ?? [], ]; } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * Download a file from cPanel */ public function downloadFile(string $path): ?string { $url = $this->getBaseUrl().'/download?file='.urlencode($path); try { $response = Http::withHeaders([ 'Authorization' => "cpanel {$this->username}:{$this->apiToken}", ]) ->timeout(300) ->withoutVerifying() ->get($url); if ($response->successful()) { return $response->body(); } return null; } catch (Exception $e) { Log::error('cPanel download error: '.$e->getMessage()); return null; } } /** * Delete/revoke the current API token from cPanel * This should be called after migration is complete for security */ public function revokeApiToken(): array { try { Log::info('Attempting to revoke cPanel API token'); // First, list all tokens to find the current one $listResult = $this->uapi('Tokens', 'list'); $tokens = $listResult['result']['data'] ?? []; Log::info('Found API tokens', ['count' => count($tokens)]); // The current token should be one of these - we'll try to identify and revoke it // cPanel doesn't directly tell us which token we're using, but we can revoke by name // If the token was created with a specific name, we can target it // Try to revoke all tokens (user should create a new one if needed) // Or we can try to identify the token by checking which one works $revoked = false; foreach ($tokens as $token) { $tokenName = $token['name'] ?? ''; if (empty($tokenName)) { continue; } // Try to revoke this token try { $revokeResult = $this->uapi('Tokens', 'revoke', [ 'name' => $tokenName, ]); if (($revokeResult['result']['status'] ?? 0) == 1) { Log::info('Revoked API token', ['name' => $tokenName]); $revoked = true; // After revoking the current token, subsequent API calls will fail // So we should stop here break; } } catch (Exception $e) { // If we get an auth error, we probably just revoked our own token if (str_contains($e->getMessage(), 'Authorization') || str_contains($e->getMessage(), '401')) { Log::info('Token likely revoked (auth failed)', ['name' => $tokenName]); $revoked = true; break; } Log::warning('Failed to revoke token', ['name' => $tokenName, 'error' => $e->getMessage()]); } } if ($revoked) { return [ 'success' => true, 'message' => 'API token revoked successfully', ]; } return [ 'success' => false, 'message' => 'Could not identify token to revoke', ]; } catch (Exception $e) { // Auth failure after revoke is actually success if (str_contains($e->getMessage(), 'Authorization') || str_contains($e->getMessage(), '401')) { return [ 'success' => true, 'message' => 'API token revoked (connection closed)', ]; } Log::error('Failed to revoke API token: '.$e->getMessage()); return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * Revoke a specific API token by name */ public function revokeApiTokenByName(string $tokenName): array { try { Log::info('Revoking cPanel API token by name', ['name' => $tokenName]); $result = $this->uapi('Tokens', 'revoke', [ 'name' => $tokenName, ]); if (($result['result']['status'] ?? 0) == 1) { return [ 'success' => true, 'message' => 'API token revoked successfully', ]; } $error = $result['result']['errors'][0] ?? 'Unknown error'; throw new Exception($error); } catch (Exception $e) { // Auth failure after revoke means it worked if (str_contains($e->getMessage(), 'Authorization') || str_contains($e->getMessage(), '401')) { return [ 'success' => true, 'message' => 'API token revoked', ]; } Log::error('Failed to revoke API token: '.$e->getMessage()); return [ 'success' => false, 'message' => $e->getMessage(), ]; } } /** * Get a comprehensive migration summary */ public function getMigrationSummary(): array { $summary = [ 'success' => true, 'domains' => [ 'main' => '', 'addon' => [], 'sub' => [], 'parked' => [], ], 'databases' => [], 'email_accounts' => [], 'forwarders' => [], 'ssl_certificates' => [], 'cron_jobs' => [], 'errors' => [], ]; // Get domains try { $domains = $this->listDomains(); if ($domains['success']) { $summary['domains'] = [ 'main' => $domains['main_domain'] ?? '', 'addon' => $domains['addon_domains'] ?? [], 'sub' => $domains['sub_domains'] ?? [], 'parked' => $domains['parked_domains'] ?? [], ]; } else { $summary['errors'][] = 'Domains: '.($domains['message'] ?? 'Unknown error'); } } catch (Exception $e) { Log::warning('cPanel migration - failed to list domains: '.$e->getMessage()); $summary['errors'][] = 'Domains: '.$e->getMessage(); } // Get databases try { $databases = $this->listDatabases(); if ($databases['success']) { $summary['databases'] = $databases['databases'] ?? []; } else { $summary['errors'][] = 'Databases: '.($databases['message'] ?? 'Unknown error'); } } catch (Exception $e) { Log::warning('cPanel migration - failed to list databases: '.$e->getMessage()); $summary['errors'][] = 'Databases: '.$e->getMessage(); } // Get email accounts try { $emails = $this->listEmailAccounts(); if ($emails['success']) { $summary['email_accounts'] = $emails['accounts'] ?? []; } else { $summary['errors'][] = 'Email: '.($emails['message'] ?? 'Unknown error'); } } catch (Exception $e) { Log::warning('cPanel migration - failed to list email accounts: '.$e->getMessage()); $summary['errors'][] = 'Email: '.$e->getMessage(); } // Get forwarders try { $forwarders = $this->listForwarders(); if ($forwarders['success']) { $summary['forwarders'] = $forwarders['forwarders'] ?? []; } } catch (Exception $e) { Log::warning('cPanel migration - failed to list forwarders: '.$e->getMessage()); } // Get SSL certificates try { $ssl = $this->listSslCertificates(); if ($ssl['success']) { $summary['ssl_certificates'] = $ssl['certificates'] ?? []; } } catch (Exception $e) { Log::warning('cPanel migration - failed to list SSL certificates: '.$e->getMessage()); } // Get cron jobs try { $cron = $this->listCronJobs(); if ($cron['success']) { $summary['cron_jobs'] = $cron['cron_jobs'] ?? []; } } catch (Exception $e) { Log::warning('cPanel migration - failed to list cron jobs: '.$e->getMessage()); } // Log summary for debugging Log::info('cPanel migration summary', [ 'domains_main' => $summary['domains']['main'], 'domains_addon_count' => count($summary['domains']['addon']), 'databases_count' => count($summary['databases']), 'email_accounts_count' => count($summary['email_accounts']), 'errors' => $summary['errors'], ]); return $summary; } }