Files
jabali-panel/app/Services/Migration/WhmApiService.php
2026-01-24 19:36:46 +02:00

963 lines
33 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services\Migration;
use Exception;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class WhmApiService
{
private string $hostname;
private string $username;
private string $apiToken;
private int $port;
private bool $ssl;
public function __construct(
string $hostname,
string $username,
string $apiToken,
int $port = 2087,
bool $ssl = true
) {
$this->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 WHMAPI1 request
*/
public function whmapi(string $function, array $params = [], int $timeout = 120): array
{
$url = $this->getBaseUrl()."/json-api/{$function}";
$params['api.version'] = 1;
Log::info('WHM API request', ['function' => $function, 'url' => $url]);
try {
$response = Http::withHeaders([
'Authorization' => "whm {$this->username}:{$this->apiToken}",
])
->timeout($timeout)
->connectTimeout(10)
->withoutVerifying()
->get($url, $params);
if (! $response->successful()) {
throw new Exception('WHM API request failed: '.$response->status());
}
$data = $response->json();
// WHM API returns metadata.result = 1 on success
if (($data['metadata']['result'] ?? 0) !== 1) {
$reason = $data['metadata']['reason'] ?? 'Unknown error';
throw new Exception("WHM API error: {$reason}");
}
return $data;
} catch (Exception $e) {
Log::error('WHM API error', ['function' => $function, 'error' => $e->getMessage()]);
throw $e;
}
}
/**
* Test connection to WHM
*/
public function testConnection(): array
{
try {
$result = $this->whmapi('version');
return [
'success' => true,
'version' => $result['data']['version'] ?? 'Unknown',
];
} catch (Exception $e) {
return [
'success' => false,
'message' => $e->getMessage(),
];
}
}
/**
* List all cPanel accounts on the WHM server
*/
public function listAccounts(): array
{
try {
$result = $this->whmapi('listaccts');
$accounts = $result['data']['acct'] ?? [];
return [
'success' => true,
'accounts' => array_map(fn ($acct) => [
'user' => $acct['user'] ?? '',
'domain' => $acct['domain'] ?? '',
'email' => $acct['email'] ?? '',
'diskused' => $acct['diskused'] ?? '0M',
'disklimit' => $acct['disklimit'] ?? 'unlimited',
'plan' => $acct['plan'] ?? '',
'startdate' => $acct['startdate'] ?? '',
'suspended' => ($acct['suspended'] ?? 0) == 1,
'ip' => $acct['ip'] ?? '',
'shell' => $acct['shell'] ?? '',
'owner' => $acct['owner'] ?? '',
], $accounts),
];
} catch (Exception $e) {
return [
'success' => false,
'message' => $e->getMessage(),
];
}
}
/**
* Get summary for a specific cPanel account
*/
public function getAccountSummary(string $user): array
{
try {
$result = $this->whmapi('accountsummary', ['user' => $user]);
$acct = $result['data']['acct'][0] ?? [];
return [
'success' => true,
'account' => [
'user' => $acct['user'] ?? '',
'domain' => $acct['domain'] ?? '',
'email' => $acct['email'] ?? '',
'diskused' => $acct['diskused'] ?? '0M',
'disklimit' => $acct['disklimit'] ?? 'unlimited',
'plan' => $acct['plan'] ?? '',
'suspended' => ($acct['suspended'] ?? 0) == 1,
'ip' => $acct['ip'] ?? '',
'partition' => $acct['partition'] ?? '',
'homedir' => $acct['homedir'] ?? "/home/{$user}",
],
];
} catch (Exception $e) {
return [
'success' => false,
'message' => $e->getMessage(),
];
}
}
/**
* Make an API2 request through WHM for a specific user
* Uses /json-api/cpanel endpoint with cpanel_jsonapi_apiversion=2
*/
public function api2(string $user, string $module, string $function, array $params = [], int $timeout = 120): array
{
$url = $this->getBaseUrl().'/json-api/cpanel';
// Build query params with correct WHM API2 proxy parameter names
$queryParams = [
'cpanel_jsonapi_user' => $user,
'cpanel_jsonapi_module' => $module,
'cpanel_jsonapi_func' => $function,
'cpanel_jsonapi_apiversion' => 2,
];
// Add function-specific parameters
foreach ($params as $key => $value) {
$queryParams[$key] = $value;
}
Log::info('WHM API2 request', [
'user' => $user,
'module' => $module,
'function' => $function,
]);
try {
$response = Http::withHeaders([
'Authorization' => "whm {$this->username}:{$this->apiToken}",
])
->timeout($timeout)
->connectTimeout(10)
->withoutVerifying()
->get($url, $queryParams);
if (! $response->successful()) {
throw new Exception('WHM API2 request failed: '.$response->status());
}
$data = $response->json();
Log::info('WHM API2 response', ['user' => $user, 'module' => $module, 'function' => $function]);
// Return data - let calling function handle cpanelresult errors
// (some errors like "already exists" should be handled gracefully)
return $data;
} catch (Exception $e) {
Log::error('WHM API2 error', ['user' => $user, 'module' => $module, 'function' => $function, 'error' => $e->getMessage()]);
throw $e;
}
}
/**
* Make a UAPI request through WHM for a specific user
* Uses /json-api/cpanel endpoint with cpanel_jsonapi_apiversion=3
*/
public function uapi(string $user, string $module, string $function, array $params = [], int $timeout = 120): array
{
// WHM UAPI proxy endpoint is /json-api/cpanel with apiversion=3
$url = $this->getBaseUrl().'/json-api/cpanel';
// Build query params with correct WHM UAPI proxy parameter names
$queryParams = [
'cpanel_jsonapi_user' => $user,
'cpanel_jsonapi_module' => $module,
'cpanel_jsonapi_func' => $function,
'cpanel_jsonapi_apiversion' => 3,
];
// Add function-specific parameters
foreach ($params as $key => $value) {
$queryParams[$key] = $value;
}
Log::info('WHM UAPI request', [
'user' => $user,
'module' => $module,
'function' => $function,
'url' => $url,
'params' => array_keys($queryParams),
]);
try {
$response = Http::withHeaders([
'Authorization' => "whm {$this->username}:{$this->apiToken}",
])
->timeout($timeout)
->connectTimeout(10)
->withoutVerifying()
->get($url, $queryParams);
if (! $response->successful()) {
throw new Exception('WHM UAPI request failed: '.$response->status());
}
$data = $response->json();
Log::info('WHM UAPI response', ['user' => $user, 'module' => $module, 'function' => $function, 'data' => $data]);
// UAPI through WHM returns result.status = 1 on success
// But the structure may be wrapped differently
if (isset($data['result']['status'])) {
if (($data['result']['status'] ?? 0) !== 1) {
$errors = $data['result']['errors'] ?? [];
$errorMsg = is_array($errors) ? ($errors[0] ?? 'Unknown error') : $errors;
throw new Exception("UAPI error: {$errorMsg}");
}
} elseif (isset($data['cpanelresult']['error'])) {
// Legacy response format
throw new Exception('UAPI error: '.$data['cpanelresult']['error']);
} elseif (isset($data['error'])) {
throw new Exception('UAPI error: '.$data['error']);
}
return $data;
} catch (Exception $e) {
Log::error('WHM UAPI error', ['user' => $user, 'module' => $module, 'function' => $function, 'error' => $e->getMessage()]);
throw $e;
}
}
/**
* Create a full backup for a specific user via WHM
* Uses UAPI through WHM proxy
*/
public function createBackupForUser(string $user): array
{
try {
// Use UAPI Backup::fullbackup_to_homedir via WHM proxy
$result = $this->uapi($user, 'Backup', 'fullbackup_to_homedir', [], 300);
Log::info('WHM createBackupForUser response', ['user' => $user, 'result' => $result]);
// Extract data from potentially different response structures
$data = $result['result']['data'] ?? $result['cpanelresult']['data'] ?? $result['data'] ?? [];
// Handle array or object data
if (is_array($data) && isset($data[0])) {
$data = $data[0];
}
return [
'success' => true,
'message' => 'Backup initiated',
'pid' => $data['pid'] ?? $data['backup_id'] ?? null,
'data' => $data,
];
} catch (Exception $e) {
Log::error('WHM createBackupForUser error', ['user' => $user, 'error' => $e->getMessage()]);
return [
'success' => false,
'message' => $e->getMessage(),
];
}
}
/**
* List backup files for a user via WHM UAPI
*/
public function listBackupsForUser(string $user): array
{
try {
// Get account info to find homedir
$acctResult = $this->whmapi('accountsummary', ['user' => $user]);
$acct = $acctResult['data']['acct'][0] ?? [];
$homedir = $acct['homedir'] ?? "/home/{$user}";
// Use UAPI Backup::list_backups via WHM proxy
$result = $this->uapi($user, 'Backup', 'list_backups');
// Extract backups from potentially different response structures
$backups = $result['result']['data'] ?? $result['cpanelresult']['data'] ?? $result['data'] ?? [];
// Format the backups
$formattedBackups = [];
foreach ($backups as $backup) {
$file = $backup['file'] ?? $backup['backupID'] ?? $backup['backup'] ?? '';
if (empty($file)) {
continue;
}
$formattedBackups[] = [
'file' => $file,
'status' => $backup['status'] ?? 'complete',
'time' => $backup['mtime'] ?? $backup['time'] ?? 0,
'localtime' => $backup['localtime'] ?? '',
'path' => "{$homedir}/{$file}",
];
}
return [
'success' => true,
'homedir' => $homedir,
'backups' => $formattedBackups,
];
} catch (Exception $e) {
return [
'success' => false,
'message' => $e->getMessage(),
];
}
}
/**
* Check backup status for a user by looking for backup files
*/
public function getBackupStatusForUser(string $user): array
{
try {
// Get account info first
$acctResult = $this->whmapi('accountsummary', ['user' => $user]);
$acct = $acctResult['data']['acct'][0] ?? [];
$homedir = $acct['homedir'] ?? "/home/{$user}";
// Use UAPI Fileman::list_files to check homedir via WHM proxy
$result = $this->uapi($user, 'Fileman', 'list_files', [
'dir' => $homedir,
'include_mime' => 0,
'include_permissions' => 0,
'include_hash' => 0,
'include_content' => 0,
]);
// Extract from potentially different response structures
$files = $result['result']['data'] ?? $result['cpanelresult']['data'] ?? $result['data'] ?? [];
$backupFiles = [];
$inProgress = false;
foreach ($files as $file) {
$name = $file['file'] ?? $file['name'] ?? $file['fullpath'] ?? '';
// Extract just the filename if full path
if (str_contains($name, '/')) {
$name = basename($name);
}
// cPanel backup files: 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' => (int) ($file['size'] ?? 0),
'mtime' => (int) ($file['mtime'] ?? $file['ctime'] ?? 0),
'path' => "{$homedir}/{$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,
'homedir' => $homedir,
];
} catch (Exception $e) {
return [
'success' => false,
'message' => $e->getMessage(),
];
}
}
/**
* Get migration summary for a specific user via WHM UAPI
*/
public function getUserMigrationSummary(string $user): array
{
$summary = [
'success' => true,
'user' => $user,
'domains' => [
'main' => '',
'addon' => [],
'sub' => [],
'parked' => [],
],
'databases' => [],
'email_accounts' => [],
'email_forwarders' => [],
'ssl_certificates' => [],
'errors' => [],
];
// Get domains
try {
$result = $this->uapi($user, 'DomainInfo', 'list_domains');
// Extract from potentially different response structures
$data = $result['result']['data'] ?? $result['cpanelresult']['data'] ?? $result['data'] ?? [];
// Handle array or single object
if (is_array($data) && isset($data[0]) && is_array($data[0])) {
$data = $data[0];
}
$summary['domains'] = [
'main' => $data['main_domain'] ?? '',
'addon' => $data['addon_domains'] ?? [],
'sub' => $data['sub_domains'] ?? [],
'parked' => $data['parked_domains'] ?? $data['alias_domains'] ?? [],
];
} catch (Exception $e) {
Log::warning("WHM migration - failed to list domains for {$user}: ".$e->getMessage());
$summary['errors'][] = 'Domains: '.$e->getMessage();
}
// Get databases
try {
$result = $this->uapi($user, 'Mysql', 'list_databases');
$summary['databases'] = $result['result']['data'] ?? $result['cpanelresult']['data'] ?? $result['data'] ?? [];
} catch (Exception $e) {
Log::warning("WHM migration - failed to list databases for {$user}: ".$e->getMessage());
$summary['errors'][] = 'Databases: '.$e->getMessage();
}
// Get email accounts
try {
$result = $this->uapi($user, 'Email', 'list_pops_with_disk');
$summary['email_accounts'] = $result['result']['data'] ?? $result['cpanelresult']['data'] ?? $result['data'] ?? [];
} catch (Exception $e) {
Log::warning("WHM migration - failed to list email accounts for {$user}: ".$e->getMessage());
$summary['errors'][] = 'Email: '.$e->getMessage();
}
// Get email forwarders
try {
$result = $this->uapi($user, 'Email', 'list_forwarders');
$summary['email_forwarders'] = $result['result']['data'] ?? $result['cpanelresult']['data'] ?? $result['data'] ?? [];
} catch (Exception $e) {
Log::warning("WHM migration - failed to list email forwarders for {$user}: ".$e->getMessage());
// Forwarders are optional, don't add to errors
}
// Get SSL certificates
try {
$result = $this->uapi($user, 'SSL', 'list_certs');
$summary['ssl_certificates'] = $result['result']['data'] ?? $result['cpanelresult']['data'] ?? $result['data'] ?? [];
} catch (Exception $e) {
Log::warning("WHM migration - failed to list SSL certificates for {$user}: ".$e->getMessage());
}
return $summary;
}
/**
* Get WHM server hostname
*/
public function getHostname(): string
{
return $this->hostname;
}
/**
* Get the authenticated WHM username
*/
public function getUsername(): string
{
return $this->username;
}
/**
* Get WHM version information
*/
public function getVersion(): array
{
try {
$result = $this->whmapi('version');
return [
'success' => true,
'version' => $result['data']['version'] ?? 'Unknown',
];
} catch (Exception $e) {
return [
'success' => false,
'message' => $e->getMessage(),
];
}
}
/**
* Download a file from a cPanel user's homedir via WHM
* Uses cPanel session-based download for reliability
*/
public function downloadFileFromUser(string $user, string $remotePath, string $localPath, ?callable $progressCallback = null): array
{
Log::info('WHM download starting', ['user' => $user, 'remote' => $remotePath, 'local' => $localPath]);
try {
// Step 1: Create a cPanel session for the user
$sessionResult = $this->whmapi('create_user_session', [
'user' => $user,
'service' => 'cpaneld',
]);
$sessionUrl = $sessionResult['data']['url'] ?? null;
if (! $sessionUrl) {
throw new Exception('Failed to create cPanel session');
}
Log::info('WHM session created', ['user' => $user, 'session_url' => $sessionUrl]);
// Extract the session token and base URL from the session URL
// Session URL format: https://hostname:2083/cpsess1234567890/...
if (preg_match('#^(https?://[^/]+)(/.*)$#', $sessionUrl, $matches)) {
$baseUrl = $matches[1];
$sessionPath = $matches[2];
// Extract session token from path (cpsessXXXXXX)
if (preg_match('#/(cpsess[^/]+)/#', $sessionPath, $sessMatches)) {
$sessionToken = $sessMatches[1];
} else {
throw new Exception('Could not extract session token from URL');
}
} else {
throw new Exception('Invalid session URL format');
}
// Step 2: Build the download URL using the session
// cPanel download URL format: /cpsessXXXX/download?file=/path/to/file
$downloadUrl = "{$baseUrl}/{$sessionToken}/download?skipencode=1&file=".urlencode($remotePath);
Log::info('WHM download URL', ['url' => $downloadUrl]);
// Step 3: Download using curl for better reliability
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $downloadUrl,
CURLOPT_RETURNTRANSFER => false,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_TIMEOUT => 3600,
CURLOPT_CONNECTTIMEOUT => 30,
]);
// Ensure local directory exists
$localDir = dirname($localPath);
if (! is_dir($localDir)) {
mkdir($localDir, 0755, true);
}
// Open local file for writing
$fp = fopen($localPath, 'wb');
if (! $fp) {
curl_close($ch);
throw new Exception("Failed to open local file for writing: {$localPath}");
}
curl_setopt($ch, CURLOPT_FILE, $fp);
// Execute download
$result = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
fclose($fp);
// Check for errors
if ($result === false || ! empty($error)) {
@unlink($localPath);
throw new Exception("cURL error: {$error}");
}
if ($httpCode !== 200) {
// Read what was downloaded to check for error message
$content = file_get_contents($localPath);
@unlink($localPath);
if (str_contains($content, '<html') || str_contains($content, 'error')) {
throw new Exception("HTTP {$httpCode}: Server returned error page");
}
throw new Exception("HTTP {$httpCode}: Download failed");
}
// Verify file was downloaded
if (! file_exists($localPath) || filesize($localPath) === 0) {
throw new Exception('Download failed - file is empty or missing');
}
$fileSize = filesize($localPath);
Log::info('WHM download completed', [
'user' => $user,
'remote' => $remotePath,
'local' => $localPath,
'size' => $fileSize,
]);
return [
'success' => true,
'path' => $localPath,
'size' => $fileSize,
];
} catch (Exception $e) {
Log::error('WHM download error: '.$e->getMessage(), [
'user' => $user,
'remote' => $remotePath,
'local' => $localPath,
]);
// Clean up partial download
if (file_exists($localPath)) {
@unlink($localPath);
}
return [
'success' => false,
'message' => $e->getMessage(),
];
}
}
/**
* Import an SSH private key to a cPanel user account via WHM
* Uses API2 SSH::importkey through WHM proxy
*/
public function importSshPrivateKey(string $user, string $keyName, string $privateKey, string $passphrase = ''): array
{
try {
Log::info('WHM: Importing SSH private key to cPanel user', ['user' => $user, 'key_name' => $keyName]);
$params = [
'key' => $privateKey,
'name' => $keyName,
];
if (! empty($passphrase)) {
$params['pass'] = $passphrase;
}
$result = $this->api2($user, 'SSH', 'importkey', $params);
Log::info('WHM SSH private key import response', ['user' => $user, 'result' => $result]);
$data = $result['cpanelresult']['data'][0] ?? [];
$apiError = $result['cpanelresult']['error'] ?? '';
// Check for "already exists" which is OK - extract actual key name if different
$reasonText = $apiError ?: ($data['reason'] ?? '');
if (str_contains($reasonText, 'already exists')) {
// Extract the actual key name if provided (format: "already exists as keyname")
$actualKeyName = $keyName;
if (preg_match('/already exists as ([^\s.]+)/', $reasonText, $matches)) {
$actualKeyName = $matches[1];
}
return [
'success' => true,
'message' => 'SSH key already exists',
'actual_key_name' => $actualKeyName,
];
}
// Check for API-level error
if ($apiError) {
throw new Exception($apiError);
}
// Check for success
$eventResult = $result['cpanelresult']['event']['result'] ?? null;
if ($eventResult == 1 || (isset($data['result']) && $data['result'] == 1)) {
return [
'success' => true,
'message' => 'SSH private key imported successfully',
];
}
$errorMsg = $data['reason'] ?? $data['error'] ?? 'Failed to import SSH key';
throw new Exception($errorMsg);
} catch (Exception $e) {
Log::error('WHM SSH private key import failed', ['user' => $user, 'error' => $e->getMessage()]);
return [
'success' => false,
'message' => $e->getMessage(),
];
}
}
/**
* Authorize an SSH key for a cPanel user via WHM
* Uses API2 SSH::authkey through WHM proxy
*/
public function authorizeSshKey(string $user, string $keyName): array
{
try {
Log::info('WHM: Authorizing SSH key for cPanel user', ['user' => $user, 'key_name' => $keyName]);
$result = $this->api2($user, 'SSH', 'authkey', [
'key' => $keyName,
'action' => 'authorize',
]);
Log::info('WHM SSH authkey response', ['user' => $user, '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 error
if ($apiError) {
throw new Exception($apiError);
}
// Check for success
$eventResult = $result['cpanelresult']['event']['result'] ?? null;
if ($eventResult == 1 || (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('WHM SSH key authorization failed', ['user' => $user, 'error' => $e->getMessage()]);
return [
'success' => false,
'message' => $e->getMessage(),
];
}
}
/**
* Create a full backup and upload to remote server via SCP with key authentication
* Uses UAPI Backup::fullbackup_to_scp_with_key through WHM proxy
*/
public function createBackupToScpWithKey(
string $user,
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' => '',
];
Log::info('WHM: Initiating backup to SCP for user', [
'cpanel_user' => $user,
'host' => $remoteHost,
'remote_user' => $remoteUser,
'path' => $remotePath,
'key_name' => $keyName,
]);
$result = $this->uapi($user, 'Backup', 'fullbackup_to_scp_with_key', $params, 120);
// Extract data from response
$data = $result['result']['data'] ?? $result['cpanelresult']['data'] ?? [];
return [
'success' => true,
'message' => 'Backup initiated with SCP transfer',
'pid' => $data['pid'] ?? null,
'data' => $data,
];
} catch (Exception $e) {
Log::error('WHM backup to SCP failed', ['user' => $user, 'error' => $e->getMessage()]);
return [
'success' => false,
'message' => $e->getMessage(),
];
}
}
/**
* Convert API migration summary data to agent-compatible format
*/
public function convertApiDataToAgentFormat(array $apiData): array
{
$result = [
'domains' => [],
'databases' => [],
'mailboxes' => [],
'forwarders' => [],
'ssl_certificates' => [],
];
// Convert domains
$domains = $apiData['domains'] ?? [];
if (! empty($domains['main'])) {
$result['domains'][] = ['name' => $domains['main'], 'type' => 'main'];
}
foreach ($domains['addon'] ?? [] as $domain) {
$result['domains'][] = ['name' => $domain, 'type' => 'addon'];
}
foreach ($domains['sub'] ?? [] as $domain) {
$result['domains'][] = ['name' => $domain, 'type' => 'sub'];
}
foreach ($domains['parked'] ?? [] as $domain) {
$result['domains'][] = ['name' => $domain, 'type' => 'parked'];
}
// Convert databases
foreach ($apiData['databases'] ?? [] as $db) {
$dbName = is_array($db) ? ($db['database'] ?? $db['name'] ?? '') : $db;
if ($dbName) {
$result['databases'][] = ['name' => $dbName, 'file' => "mysql/{$dbName}.sql"];
}
}
// Convert email accounts to mailboxes format
foreach ($apiData['email_accounts'] ?? [] as $email) {
$emailAddr = is_array($email) ? ($email['email'] ?? '') : $email;
if ($emailAddr && str_contains($emailAddr, '@')) {
[$localPart, $domain] = explode('@', $emailAddr, 2);
$result['mailboxes'][] = [
'email' => $emailAddr,
'local_part' => $localPart,
'domain' => $domain,
];
}
}
// Convert email forwarders
// cPanel forwarder format: {'dest' => 'dest@example.com', 'forward' => 'source@domain.com', 'html_dest' => '...'}
foreach ($apiData['email_forwarders'] ?? [] as $forwarder) {
if (is_array($forwarder)) {
$source = $forwarder['forward'] ?? $forwarder['source'] ?? '';
$dest = $forwarder['dest'] ?? $forwarder['destination'] ?? '';
if ($source && str_contains($source, '@') && $dest) {
[$localPart, $domain] = explode('@', $source, 2);
$result['forwarders'][] = [
'email' => $source,
'local_part' => $localPart,
'domain' => $domain,
'destinations' => $dest, // Will be parsed in the restore function
];
}
}
}
// Convert SSL certificates
foreach ($apiData['ssl_certificates'] ?? [] as $cert) {
if (is_array($cert)) {
$domain = $cert['domain'] ?? $cert['friendly_name'] ?? null;
if (! $domain && ! empty($cert['domains'])) {
$domains = is_array($cert['domains']) ? $cert['domains'] : explode(',', $cert['domains']);
$domain = trim($domains[0] ?? '');
}
if ($domain) {
$result['ssl_certificates'][] = [
'domain' => $domain,
'has_key' => true,
'has_cert' => true,
];
}
} elseif (is_string($cert) && ! empty($cert)) {
$result['ssl_certificates'][] = [
'domain' => $cert,
'has_key' => true,
'has_cert' => true,
];
}
}
return $result;
}
}