agc-chatbot/serve.py

180 lines
7.4 KiB
Python

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]} <port_number>')
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]} <port_number>')
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)