#!/usr/bin/env python3 """ Simple HTTP server for SearchEngine frontend. """ import http.server import socketserver import os import sys import logging import time from collections import defaultdict, deque from pathlib import Path from dotenv import load_dotenv # Load .env file project_root = Path(__file__).parent.parent load_dotenv(project_root / '.env') # Get API_BASE_URL from environment API_BASE_URL = os.getenv('API_BASE_URL', 'http://localhost:6002') # Change to frontend directory frontend_dir = os.path.join(os.path.dirname(__file__), '../frontend') os.chdir(frontend_dir) # Get port from environment variable or default PORT = int(os.getenv('PORT', 6003)) # Configure logging to suppress scanner noise logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s') class RateLimitingMixin: """Mixin for rate limiting requests by IP address.""" request_counts = defaultdict(deque) rate_limit = 100 # requests per minute window = 60 # seconds @classmethod def is_rate_limited(cls, ip): now = time.time() # Clean old requests while cls.request_counts[ip] and cls.request_counts[ip][0] < now - cls.window: cls.request_counts[ip].popleft() # Check rate limit if len(cls.request_counts[ip]) > cls.rate_limit: return True cls.request_counts[ip].append(now) return False class MyHTTPRequestHandler(http.server.SimpleHTTPRequestHandler, RateLimitingMixin): """Custom request handler with CORS support and robust error handling.""" def do_GET(self): """Handle GET requests with API config injection.""" path = self.path.split('?')[0] # Route / to index.html if path == '/' or path == '': self.path = '/index.html' + (self.path.split('?', 1)[1] if '?' in self.path else '') # Inject API config for HTML files if self.path.endswith('.html'): self._serve_html_with_config() else: super().do_GET() def _serve_html_with_config(self): """Serve HTML with API_BASE_URL injected.""" try: file_path = self.path.lstrip('/') if not os.path.exists(file_path): self.send_error(404) return with open(file_path, 'r', encoding='utf-8') as f: html = f.read() # Inject API_BASE_URL before app.js config_script = f'\n ' html = html.replace('