from app.utils.browser_utils import safe_browser_operation import asyncio from urllib.parse import unquote async def visit_url_service(decoded_url): """Service function to visit a URL and get its content""" print(f"Visiting URL: {decoded_url}") # Define the operation to perform with the browser async def visit_operation(page): try: response = await page.goto(decoded_url, wait_until='networkidle', timeout=30000) if not response: print(f"Warning: No response object returned for {decoded_url}") cookie_buttons = [ 'Accept all', 'Accepteer', 'Accepteren', 'Accept' ] try: for button in cookie_buttons: element = await page.query_selector(f'text="{button}"') if element: await element.click() print(f"{button} button found and clicked") break except Exception as e: print(f"Error during cookie acceptance: {e}") print("No cookies to accept") # Get page content content = await page.content() return {"status": "success", "content": content} except Exception as e: print(f"Error during page navigation: {e}") # Try to get content anyway try: content = await page.content() return {"status": "partial", "content": content, "error": str(e)} except: raise Exception(f"Failed to get page content: {str(e)}") # Perform the operation return await safe_browser_operation(decoded_url, visit_operation) async def extract_seo_service(decoded_url): """Service function to extract SEO information from a website""" print(f"Extracting SEO from: {decoded_url}") # Define the operation to perform with the browser async def seo_operation(page): try: response = await page.goto(decoded_url, wait_until='networkidle', timeout=30000) # Extract SEO information seo_data = await page.evaluate('''() => { const data = { title: document.title || '', description: '', canonical: '', h1: [], h2: [], images: 0, links: 0 }; // Get meta description const metaDescription = document.querySelector('meta[name="description"]'); if (metaDescription) { data.description = metaDescription.getAttribute('content') || ''; } // Get canonical link const canonicalLink = document.querySelector('link[rel="canonical"]'); if (canonicalLink) { data.canonical = canonicalLink.getAttribute('href') || ''; } // Get h1 tags document.querySelectorAll('h1').forEach(h1 => { const text = h1.innerText.trim(); if (text) data.h1.push(text); }); // Get h2 tags document.querySelectorAll('h2').forEach(h2 => { const text = h2.innerText.trim(); if (text) data.h2.push(text); }); // Count images data.images = document.querySelectorAll('img').length; // Count links data.links = document.querySelectorAll('a').length; return data; }''') result = { "status": "success", "url": decoded_url, "seo": seo_data } return result except Exception as e: print(f"Error during SEO extraction: {e}") return {"status": "error", "url": decoded_url, "error": str(e)} # Perform the operation return await safe_browser_operation(decoded_url, seo_operation) async def extract_meta_tags_service(decoded_url): """Service function to extract meta tags from a website""" print(f"Extracting meta tags from: {decoded_url}") # Define the operation to perform with the browser async def meta_tags_operation(page): try: response = await page.goto(decoded_url, wait_until='networkidle', timeout=30000) # Extract meta tags using JavaScript meta_data = await page.evaluate('''() => { const data = { meta_tags: [], open_graph: {}, twitter_card: {}, title: document.title || '' }; // Extract all meta tags document.querySelectorAll('meta').forEach(meta => { const attributes = {}; for (let attr of meta.attributes) { attributes[attr.name] = attr.value; } data.meta_tags.push(attributes); }); // Extract Open Graph tags document.querySelectorAll('meta[property^="og:"]').forEach(meta => { data.open_graph[meta.getAttribute('property')] = meta.getAttribute('content') || ''; }); // Extract Twitter card tags document.querySelectorAll('meta[name^="twitter:"]').forEach(meta => { data.twitter_card[meta.getAttribute('name')] = meta.getAttribute('content') || ''; }); return data; }''') result = { "status": "success", "url": decoded_url, "meta_tags": meta_data["meta_tags"], "open_graph": meta_data["open_graph"], "twitter_card": meta_data["twitter_card"], "title": meta_data["title"] } return result except Exception as e: print(f"Error during meta tag extraction: {e}") return {"status": "error", "url": decoded_url, "error": str(e)} # Perform the operation return await safe_browser_operation(decoded_url, meta_tags_operation) async def capture_outgoing_calls_service(decoded_url): """Service function to capture outgoing API calls from a website""" print(f"Capturing outgoing calls from: {decoded_url}") # Define the operation to perform with the browser async def outgoing_calls_operation(page): try: # List to store all network requests network_requests = [] # Set up network request listener async def handle_request(request): # Only capture API-like requests (not static assets) url = request.url method = request.method headers = request.headers # Skip static assets and common non-API requests static_extensions = ['.css', '.js', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.woff', '.woff2', '.ttf', '.eot'] if any(url.endswith(ext) for ext in static_extensions): return # Skip data URLs and blob URLs if url.startswith(('data:', 'blob:')): return # Skip same-origin requests that are likely static assets if url.startswith(decoded_url) and any(static_ext in url.lower() for static_ext in static_extensions): return # Capture the request details request_data = { "url": url, "method": method, "headers": dict(headers), "timestamp": None # Will be set when request is finished } # Store request for later processing network_requests.append(request_data) # Listen to all requests page.on("request", handle_request) # Navigate to the URL and wait for network to be idle try: await page.goto(decoded_url, wait_until='networkidle', timeout=30000) except Exception as e: print(f"Error during page navigation: {e}") # Continue anyway to capture any requests that were made # Wait a bit more to catch any delayed requests await page.wait_for_timeout(2000) # Process and categorize the requests api_calls = [] for req in network_requests: # Determine if this looks like an API call is_api_call = False api_type = "unknown" # Check for common API patterns if any(pattern in req["url"].lower() for pattern in ['/api/', '/rest/', '/graphql', '/json', '/xml']): is_api_call = True api_type = "rest" elif req["url"].endswith('.json'): is_api_call = True api_type = "json" elif 'application/json' in req["headers"].get('content-type', '').lower(): is_api_call = True api_type = "json" elif 'application/xml' in req["headers"].get('content-type', '').lower(): is_api_call = True api_type = "xml" elif req["method"] in ['POST', 'PUT', 'PATCH', 'DELETE']: # These methods are typically API calls is_api_call = True api_type = "rest" elif any(domain in req["url"] for domain in ['api.', 'rest.', 'graphql.']): is_api_call = True api_type = "rest" # Include all requests but mark API calls specifically call_info = { "url": req["url"], "method": req["method"], "is_api_call": is_api_call, "api_type": api_type if is_api_call else None, "headers": {k: v for k, v in req["headers"].items() if k.lower() not in ['user-agent', 'accept-encoding', 'accept-language', 'cache-control']} } api_calls.append(call_info) # Sort by whether it's an API call (API calls first), then by URL api_calls.sort(key=lambda x: (not x["is_api_call"], x["url"])) result = { "status": "success", "url": decoded_url, "total_requests": len(api_calls), "api_calls": [call for call in api_calls if call["is_api_call"]], "other_requests": [call for call in api_calls if not call["is_api_call"]], "summary": { "api_calls_count": len([call for call in api_calls if call["is_api_call"]]), "other_requests_count": len([call for call in api_calls if not call["is_api_call"]]), "unique_domains": len(set([call["url"].split('/')[2] for call in api_calls if len(call["url"].split('/')) > 2])) } } return result except Exception as e: print(f"Error during outgoing calls capture: {e}") return {"status": "error", "url": decoded_url, "error": str(e)} # Perform the operation return await safe_browser_operation(decoded_url, outgoing_calls_operation) async def get_resulting_url_service(decoded_url): """Service function to get the resulting URL after navigation (handles redirects)""" print(f"Getting resulting URL for: {decoded_url}") # Define the operation to perform with the browser async def resulting_url_operation(page): try: # Navigate to the URL and wait for network to be idle response = await page.goto(decoded_url, wait_until='networkidle', timeout=30000) # Get the final URL after any redirects and decode it final_url = page.url decoded_final_url = unquote(final_url) # Get response status if available status_code = response.status if response else None result = { "status": "success", "original_url": decoded_url, "resulting_url": decoded_final_url, "status_code": status_code } # Check if there was a redirect if decoded_final_url != decoded_url: result["redirected"] = True else: result["redirected"] = False return result except Exception as e: print(f"Error during URL navigation: {e}") return {"status": "error", "original_url": decoded_url, "error": str(e)} # Perform the operation return await safe_browser_operation(decoded_url, resulting_url_operation)