180 lines
7.4 KiB
Python
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) |