2025-08-18 20:00:58 +02:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
"""
|
2025-08-18 20:03:51 +02:00
|
|
|
Simple HTTP server for testing the GlitchCraft PWA
|
2025-08-18 20:00:58 +02:00
|
|
|
Run with: python3 server.py
|
2025-08-18 20:13:04 +02:00
|
|
|
Supports both IPv4 and IPv6 connections
|
2025-08-18 20:00:58 +02:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import http.server
|
|
|
|
|
import socketserver
|
2025-08-18 20:03:51 +02:00
|
|
|
import socket
|
2025-08-18 20:00:58 +02:00
|
|
|
import os
|
2025-08-18 20:13:04 +02:00
|
|
|
import sys
|
|
|
|
|
import time
|
|
|
|
|
|
2025-08-18 20:00:58 +02:00
|
|
|
|
|
|
|
|
PORT = 8000
|
|
|
|
|
# Serve from the app directory
|
|
|
|
|
DIRECTORY = os.path.join(os.path.dirname(os.path.abspath(__file__)), "app")
|
|
|
|
|
|
|
|
|
|
|
2025-08-18 20:13:04 +02:00
|
|
|
class GlitchCraftHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
|
2025-08-18 20:03:51 +02:00
|
|
|
"""Custom HTTP request handler for serving the GlitchCraft PWA."""
|
2025-08-18 20:00:58 +02:00
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super().__init__(*args, directory=DIRECTORY, **kwargs)
|
|
|
|
|
|
|
|
|
|
def end_headers(self):
|
2025-08-18 20:13:04 +02:00
|
|
|
# Add headers for PWA functionality
|
2025-08-18 20:00:58 +02:00
|
|
|
self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
|
|
|
self.send_header("Service-Worker-Allowed", "/")
|
|
|
|
|
self.send_header("X-Content-Type-Options", "nosniff")
|
2025-08-18 20:13:04 +02:00
|
|
|
# Add CORS headers for development
|
|
|
|
|
self.send_header("Access-Control-Allow-Origin", "*")
|
|
|
|
|
self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
|
|
|
|
|
self.send_header("Access-Control-Allow-Headers", "*")
|
2025-08-18 20:00:58 +02:00
|
|
|
super().end_headers()
|
|
|
|
|
|
2025-08-18 20:13:04 +02:00
|
|
|
def log_message(self, fmt, *args):
|
|
|
|
|
# Custom log format with timestamp
|
|
|
|
|
timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
|
|
|
|
print(f"[{timestamp}] {fmt % args}")
|
2025-08-18 20:00:58 +02:00
|
|
|
|
2025-08-18 20:13:04 +02:00
|
|
|
|
|
|
|
|
class DualStackTCPServer(socketserver.TCPServer):
|
|
|
|
|
"""TCP server that attempts to support both IPv4 and IPv6."""
|
|
|
|
|
|
|
|
|
|
allow_reuse_address = True
|
|
|
|
|
|
|
|
|
|
def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
|
|
|
|
|
# Try IPv6 first (with dual-stack if available)
|
|
|
|
|
self.address_family = socket.AF_INET6
|
|
|
|
|
try:
|
|
|
|
|
super().__init__(
|
|
|
|
|
server_address, RequestHandlerClass, bind_and_activate=False
|
|
|
|
|
)
|
|
|
|
|
# Try to enable dual-stack (works on most modern systems)
|
|
|
|
|
try:
|
|
|
|
|
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
|
|
|
|
|
self._is_dual_stack = True
|
|
|
|
|
except (AttributeError, OSError):
|
|
|
|
|
# Dual-stack not supported, IPv6 only
|
|
|
|
|
self._is_dual_stack = False
|
|
|
|
|
|
|
|
|
|
if bind_and_activate:
|
|
|
|
|
self.server_bind()
|
|
|
|
|
self.server_activate()
|
|
|
|
|
|
|
|
|
|
except (OSError, socket.error):
|
|
|
|
|
# IPv6 failed, fall back to IPv4
|
|
|
|
|
self.server_close()
|
|
|
|
|
self.address_family = socket.AF_INET
|
|
|
|
|
self._is_dual_stack = False
|
|
|
|
|
# Convert IPv6 address to IPv4 if needed
|
|
|
|
|
if server_address[0] == "::":
|
|
|
|
|
server_address = ("0.0.0.0", server_address[1])
|
|
|
|
|
super().__init__(server_address, RequestHandlerClass, bind_and_activate)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
"""Start the development server."""
|
|
|
|
|
print("Starting GlitchCraft development server...")
|
|
|
|
|
print(f"Serving files from: {DIRECTORY}")
|
|
|
|
|
print(f"Port: {PORT}")
|
|
|
|
|
|
|
|
|
|
# Check if app directory exists
|
|
|
|
|
if not os.path.exists(DIRECTORY):
|
|
|
|
|
print(f"Error: Directory {DIRECTORY} does not exist!")
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
# Try different host configurations
|
|
|
|
|
hosts_to_try = [
|
|
|
|
|
("::", "IPv6 dual-stack"),
|
|
|
|
|
("0.0.0.0", "IPv4 all interfaces"),
|
|
|
|
|
("127.0.0.1", "IPv4 localhost only"),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
server = None
|
|
|
|
|
for host, description in hosts_to_try:
|
2025-08-18 20:00:58 +02:00
|
|
|
try:
|
2025-08-18 20:13:04 +02:00
|
|
|
server = DualStackTCPServer((host, PORT), GlitchCraftHTTPRequestHandler)
|
|
|
|
|
print(f"✓ Server started using {description}")
|
|
|
|
|
|
|
|
|
|
# Print access URLs based on server configuration
|
|
|
|
|
print("\nAccess your app at:")
|
|
|
|
|
if server.address_family == socket.AF_INET6:
|
|
|
|
|
if hasattr(server, "_is_dual_stack") and getattr(
|
|
|
|
|
server, "_is_dual_stack", False
|
|
|
|
|
):
|
|
|
|
|
print(f" http://localhost:{PORT}/")
|
|
|
|
|
print(f" http://127.0.0.1:{PORT}/ (IPv4)")
|
|
|
|
|
print(f" http://[::1]:{PORT}/ (IPv6)")
|
|
|
|
|
print(f" http://<your-ip>:{PORT}/ (network)")
|
|
|
|
|
else:
|
|
|
|
|
print(f" http://[::1]:{PORT}/ (IPv6 only)")
|
|
|
|
|
print(f" http://localhost:{PORT}/ (if supported)")
|
|
|
|
|
else:
|
|
|
|
|
if host == "0.0.0.0":
|
|
|
|
|
print(f" http://localhost:{PORT}/")
|
|
|
|
|
print(f" http://127.0.0.1:{PORT}/ (IPv4)")
|
|
|
|
|
print(f" http://<your-ip>:{PORT}/ (network)")
|
|
|
|
|
else:
|
|
|
|
|
print(f" http://localhost:{PORT}/ (localhost only)")
|
|
|
|
|
|
|
|
|
|
print("\nPress Ctrl+C to stop the server")
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
except OSError as e:
|
|
|
|
|
print(f"✗ Failed to start server on {host}: {e}")
|
|
|
|
|
if "Address already in use" in str(e):
|
|
|
|
|
print(
|
|
|
|
|
f" Port {PORT} is already in use. Try a different port or stop other servers."
|
|
|
|
|
)
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if server is None:
|
|
|
|
|
print("Failed to start server on any interface!")
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
server.serve_forever()
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
print("\nShutting down server...")
|
|
|
|
|
finally:
|
|
|
|
|
server.server_close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|