#!/usr/bin/env python3 """ Simple HTTP server for saas-search 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('