from http.server import HTTPServer, SimpleHTTPRequestHandler import os import sys import shutil import mimetypes # Ensure proper MIME types are registered mimetypes.add_type('text/css', '.css') mimetypes.add_type('application/javascript', '.js') mimetypes.add_type('text/javascript', '.js') mimetypes.add_type('image/png', '.png') mimetypes.add_type('image/x-icon', '.ico') class AGCDocumentHandler(SimpleHTTPRequestHandler): """Custom request handler for serving files from the frontend directory.""" def __init__(self, *args, directory=None, **kwargs): self.frontend_dir = os.path.join(os.getcwd(), 'frontend') if directory is None: directory = self.frontend_dir # This is important - SimpleHTTPRequestHandler expects the directory in kwargs super().__init__(*args, directory=directory, **kwargs) def do_GET(self): """Handle GET requests with special cases for root and favicon.""" original_path = self.path # Handle special paths if self.path == '/': self.path = '/index.html' # For debugging - print resolved file path resolved_path = os.path.join(self.frontend_dir, self.path.lstrip('/')) file_exists = os.path.exists(resolved_path) # Determine content type content_type = self.guess_type(resolved_path) print(f"Request: {original_path} → Resolved: {resolved_path} (Exists: {file_exists}, Type: {content_type})") # Try alternate path resolution for CSS/JS if not file_exists and (original_path.endswith('.css') or original_path.endswith('.js')): alternate_path = os.path.join(self.frontend_dir, original_path.lstrip('/')) print(f"Trying alternate path: {alternate_path}") if os.path.exists(alternate_path): self.path = original_path # Use original path print(f"Using alternate path resolution") return super().do_GET() def send_header(self, keyword, value): """Override to ensure proper content types are set.""" if keyword.lower() == 'content-type' and self.path.endswith('.css'): value = 'text/css' print(f"Setting Content-Type: {value} for {self.path}") elif keyword.lower() == 'content-type' and self.path.endswith('.js'): value = 'application/javascript' print(f"Setting Content-Type: {value} for {self.path}") super().send_header(keyword, value) def send_error(self, code, message=None, explain=None): """Enhanced error logging for 404 errors.""" if code == 404: print(f"❌ 404 ERROR: File not found: {self.path}") print(f" Resolved path: {os.path.join(self.frontend_dir, self.path.lstrip('/'))}") print(f" Referrer: {self.headers.get('Referer', 'None')}") return super().send_error(code, message, explain) def log_message(self, format, *args): """Colorize log messages for better readability.""" status_code = args[1] if status_code.startswith('2'): # 2xx status codes status_color = '\033[92m' # Green elif status_code.startswith('3'): # 3xx status codes status_color = '\033[94m' # Blue elif status_code.startswith('4'): # 4xx status codes status_color = '\033[93m' # Yellow elif status_code.startswith('5'): # 5xx status codes status_color = '\033[91m' # Red else: status_color = '\033[0m' # Default reset_color = '\033[0m' sys.stdout.write(f"{self.log_date_time_string()} {status_color}{format % args}{reset_color}\n") def verify_files(): """Verify that necessary files exist in the frontend directory.""" frontend_dir = os.path.join(os.getcwd(), 'frontend') if not os.path.exists(frontend_dir): print(f'Error: Frontend directory not found at {frontend_dir}') sys.exit(1) index_path = os.path.join(frontend_dir, 'index.html') if not os.path.exists(index_path): print(f'Error: index.html not found at {index_path}') sys.exit(1) # Check key files key_files = [ ('assets/images/favicon.ico', 'Favicon'), ('css/styles.css', 'Main CSS'), ('css/animations.css', 'Animations CSS'), ('js/api.js', 'API JavaScript'), ('js/main.js', 'Main JavaScript') ] print("Checking key files:") for file_path, desc in key_files: full_path = os.path.join(frontend_dir, file_path) exists = os.path.exists(full_path) status = "✅ Found" if exists else "❌ MISSING" print(f" {status}: {desc} ({file_path})") if file_path == 'assets/images/favicon.ico' and exists: # Copy favicon to root of frontend for browsers that look for it there root_favicon = os.path.join(frontend_dir, 'favicon.ico') if not os.path.exists(root_favicon): try: shutil.copy2(full_path, root_favicon) print(f" ✅ Copied favicon to frontend root for compatibility") except Exception as e: print(f" ❌ Could not copy favicon to root: {str(e)}") return frontend_dir def run_server(port=8888): """Run the HTTP server on the specified port.""" try: # Verify that necessary files exist frontend_dir = verify_files() # Print MIME type registrations for debugging print("\nMIME type configuration:") for ext, type in [('.html', mimetypes.guess_type('file.html')[0]), ('.css', mimetypes.guess_type('file.css')[0]), ('.js', mimetypes.guess_type('file.js')[0]), ('.png', mimetypes.guess_type('file.png')[0]), ('.ico', mimetypes.guess_type('file.ico')[0])]: print(f" {ext} → {type}") # Create and start the server handler = lambda *args, **kwargs: AGCDocumentHandler(*args, directory=frontend_dir, **kwargs) server_address = ('', port) httpd = HTTPServer(server_address, handler) print(f"\nServer running at http://localhost:{port}/") print(f"Serving files from: {frontend_dir}") print("Press Ctrl+C to stop the server") httpd.serve_forever() except PermissionError: print(f'\nError: Permission denied for port {port}') print('Try running with a different port number:') print(f'python {sys.argv[0]} ') sys.exit(1) except OSError as e: if e.errno == 98 or e.errno == 10048: # Port already in use (Linux: 98, Windows: 10048) print(f'\nError: Port {port} is already in use') print('Try running with a different port number:') print(f'python {sys.argv[0]} ') else: print(f'\nError: {str(e)}') sys.exit(1) except KeyboardInterrupt: print('\nServer stopped by user') sys.exit(0) except Exception as e: print(f'\nUnexpected error: {str(e)}') sys.exit(1) if __name__ == '__main__': # Get port from command line argument if provided port = 8888 if len(sys.argv) > 1: try: port = int(sys.argv[1]) except ValueError: print('Error: Port must be a number') sys.exit(1) run_server(port)