true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(['email' => $email, 'password' => $password]), CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_HEADER => true, CURLOPT_TIMEOUT => 30 ]); $resp = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return ['code' => $code, 'needs_2fa' => (strpos($resp, '2fa') !== false), 'raw' => substr($resp, 0, 500)]; } function cf_api_zones($email, $key, $page=1) { $ch = curl_init("https://api.cloudflare.com/client/v4/zones?page=$page&per_page=50&status=active"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "X-Auth-Email: $email", "X-Auth-Key: $key", "Content-Type: application/json" ], CURLOPT_TIMEOUT => 30 ]); $resp = curl_exec($ch); curl_close($ch); return json_decode($resp, true); } switch($action) { case 'generate_keys': // Try to get API keys for all accounts without one $accounts = pg_fetch_all(pg_query($db, "SELECT id, email, password FROM cloudflare_accounts WHERE (api_key IS NULL OR api_key='') AND password IS NOT NULL AND password!='' AND status='active' ORDER BY domains_count DESC")); if (!$accounts) { echo json_encode(['error' => 'No accounts need keys']); break; } $results = []; foreach ($accounts as $acc) { $login = cf_login_get_key($acc['email'], $acc['password']); $results[] = [ 'id' => $acc['id'], 'email' => $acc['email'], 'login_code' => $login['code'], 'needs_2fa' => $login['needs_2fa'], 'note' => 'Manual key needed - go to dash.cloudflare.com/profile/api-tokens' ]; usleep(500000); // 500ms delay between requests } echo json_encode([ 'status' => 'info', 'message' => 'CF login API requires captcha/2FA. Use manual approach or API tokens.', 'manual_steps' => [ '1. Login to each CF account at dash.cloudflare.com', '2. Go to Profile → API Tokens → Global API Key → View', '3. Copy key and use /api/cloudflare-harvester.php?action=set_key&id=X&key=KEY', '4. Then run harvest to get all domains' ], 'accounts_needing_keys' => count($accounts), 'results' => array_slice($results, 0, 5) ]); break; case 'set_key': // Manually set API key for an account $id = (int)($_GET['id'] ?? $_POST['id'] ?? 0); $key = $_GET['key'] ?? $_POST['key'] ?? ''; if (!$id || !$key) { echo json_encode(['error' => 'Need id and key']); break; } pg_query($db, "UPDATE cloudflare_accounts SET api_key='".pg_escape_string($db,$key)."', api_status='active', last_api_check=NOW() WHERE id=$id"); // Immediately try to harvest zones $acc = pg_fetch_assoc(pg_query($db, "SELECT email, api_key FROM cloudflare_accounts WHERE id=$id")); $zones_resp = cf_api_zones($acc['email'], $acc['api_key']); $domains = []; if ($zones_resp['success'] ?? false) { foreach ($zones_resp['result'] ?? [] as $zone) { $d = pg_escape_string($db, $zone['name']); $zid = pg_escape_string($db, $zone['id']); pg_query($db, "INSERT INTO cloudflare_domains(account_id, domain, zone_id, status, created_at) VALUES($id, '$d', '$zid', '{$zone['status']}', NOW()) ON CONFLICT DO NOTHING"); $domains[] = $zone['name']; } pg_query($db, "UPDATE cloudflare_accounts SET domains_count=".count($domains).", last_api_check=NOW() WHERE id=$id"); } echo json_encode([ 'status' => 'success', 'account_id' => $id, 'api_valid' => ($zones_resp['success'] ?? false), 'domains_found' => count($domains), 'domains' => $domains ]); break; case 'bulk_set_keys': // Set multiple keys at once: POST JSON array [{id:1,key:"xxx"}, ...] $input = json_decode(file_get_contents('php://input'), true); $keys = $input['keys'] ?? []; $results = []; foreach ($keys as $k) { $id = (int)$k['id']; $key = pg_escape_string($db, $k['key']); pg_query($db, "UPDATE cloudflare_accounts SET api_key='$key', api_status='active', last_api_check=NOW() WHERE id=$id"); $acc = pg_fetch_assoc(pg_query($db, "SELECT email FROM cloudflare_accounts WHERE id=$id")); $zones = cf_api_zones($acc['email'], $k['key']); $cnt = 0; if ($zones['success'] ?? false) { foreach ($zones['result'] ?? [] as $z) { $d = pg_escape_string($db, $z['name']); pg_query($db, "INSERT INTO cloudflare_domains(account_id, domain, zone_id, status, created_at) VALUES($id, '$d', '".pg_escape_string($db,$z['id'])."', '{$z['status']}', NOW()) ON CONFLICT DO NOTHING"); $cnt++; } pg_query($db, "UPDATE cloudflare_accounts SET domains_count=$cnt WHERE id=$id"); } $results[] = ['id' => $id, 'email' => $acc['email'], 'domains' => $cnt]; } echo json_encode(['status' => 'success', 'processed' => count($results), 'results' => $results]); break; case 'harvest_all': // Harvest domains from ALL accounts that have API keys $accounts = pg_fetch_all(pg_query($db, "SELECT id, email, api_key FROM cloudflare_accounts WHERE api_key IS NOT NULL AND api_key!='' AND status='active'")); if (!$accounts) { echo json_encode(['error' => 'No accounts with API keys']); break; } $total = 0; $results = []; foreach ($accounts as $acc) { $page = 1; $domains = []; do { $resp = cf_api_zones($acc['email'], $acc['api_key'], $page); if (!($resp['success'] ?? false)) break; foreach ($resp['result'] ?? [] as $z) { $d = pg_escape_string($db, $z['name']); pg_query($db, "INSERT INTO cloudflare_domains(account_id, domain, zone_id, status, created_at) VALUES({$acc['id']}, '$d', '".pg_escape_string($db,$z['id'])."', '{$z['status']}', NOW()) ON CONFLICT DO NOTHING"); pg_query($db, "INSERT INTO domains_pool(domain, provider, status, created_at) VALUES('$d', 'cloudflare', 'active', NOW()) ON CONFLICT DO NOTHING"); $domains[] = $z['name']; } $total_pages = $resp['result_info']['total_pages'] ?? 1; $page++; } while ($page <= $total_pages); pg_query($db, "UPDATE cloudflare_accounts SET domains_count=".count($domains).", last_api_check=NOW() WHERE id={$acc['id']}"); $results[] = ['id' => $acc['id'], 'email' => $acc['email'], 'domains' => count($domains)]; $total += count($domains); } echo json_encode(['status' => 'success', 'total_domains' => $total, 'accounts' => $results]); break; case 'status': $s = pg_fetch_assoc(pg_query($db, " SELECT (SELECT COUNT(*) FROM cloudflare_accounts WHERE status='active') as total_accounts, (SELECT COUNT(*) FROM cloudflare_accounts WHERE api_key IS NOT NULL AND api_key!='') as with_keys, (SELECT COUNT(*) FROM cloudflare_accounts WHERE api_key IS NULL OR api_key='') as needs_keys, (SELECT COUNT(*) FROM cloudflare_domains) as total_domains, (SELECT SUM(domains_count) FROM cloudflare_accounts) as reported_domains ")); echo json_encode(['status' => 'success', 'stats' => $s]); break; }