#!/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('