121 lines
3.5 KiB
Python
121 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Ad-hoc Flask HTTPS Server for LED Controller Webapp
|
|
Serves the webapp over HTTPS (required for Web Bluetooth API)
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
from flask import Flask, send_from_directory, send_file
|
|
from pathlib import Path
|
|
|
|
# Configuration
|
|
HOST = '0.0.0.0' # Listen on all interfaces
|
|
PORT = 5000 # HTTPS port
|
|
DEBUG = True
|
|
|
|
# Get webapp directory (one level up from tools/)
|
|
SCRIPT_DIR = Path(__file__).parent
|
|
WEBAPP_DIR = SCRIPT_DIR.parent / 'webapp'
|
|
|
|
# Verify webapp directory exists
|
|
if not WEBAPP_DIR.exists():
|
|
print(f"❌ ERROR: Webapp directory not found at {WEBAPP_DIR}")
|
|
print(f" Please run this script from the base repository directory")
|
|
sys.exit(1)
|
|
|
|
# Create Flask app
|
|
app = Flask(__name__)
|
|
|
|
@app.route('/')
|
|
def index():
|
|
"""Serve index.html"""
|
|
return send_file(WEBAPP_DIR / 'index.html')
|
|
|
|
@app.route('/app/<path:filename>')
|
|
def serve_app(filename):
|
|
"""Serve files from app/ directory"""
|
|
return send_from_directory(WEBAPP_DIR / 'app', filename)
|
|
|
|
@app.route('/css/<path:filename>')
|
|
def serve_css(filename):
|
|
"""Serve files from css/ directory"""
|
|
return send_from_directory(WEBAPP_DIR / 'css', filename)
|
|
|
|
@app.route('/data/<path:filename>')
|
|
def serve_data(filename):
|
|
"""Serve files from data/ directory"""
|
|
return send_from_directory(WEBAPP_DIR / 'data', filename)
|
|
|
|
@app.route('/favicon.ico')
|
|
def favicon():
|
|
"""Serve favicon"""
|
|
return send_file(WEBAPP_DIR / 'data' / 'favicon.ico')
|
|
|
|
def print_banner():
|
|
"""Print startup banner with instructions"""
|
|
print("=" * 70)
|
|
print(" 🚀 LED Controller HTTPS Development Server")
|
|
print("=" * 70)
|
|
print()
|
|
print("📱 Web Bluetooth requires HTTPS!")
|
|
print(" This server provides a self-signed certificate for development.")
|
|
print()
|
|
print("🌐 Access the webapp at:")
|
|
print(f" https://localhost:{PORT}")
|
|
print(f" https://127.0.0.1:{PORT}")
|
|
print(f" https://<your-ip>:{PORT}")
|
|
print()
|
|
print("⚠️ Browser Security Warning:")
|
|
print(" You'll see a 'Not Secure' warning - this is normal!")
|
|
print(" Click 'Advanced' → 'Proceed to localhost' (or similar)")
|
|
print()
|
|
print("🔧 To stop the server: Press Ctrl+C")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
def get_local_ip():
|
|
"""Get local IP address for convenience"""
|
|
import socket
|
|
try:
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
s.connect(("8.8.8.8", 80))
|
|
ip = s.getsockname()[0]
|
|
s.close()
|
|
return ip
|
|
except:
|
|
return "unknown"
|
|
|
|
if __name__ == '__main__':
|
|
print_banner()
|
|
|
|
# Print local IP for convenience
|
|
local_ip = get_local_ip()
|
|
if local_ip != "unknown":
|
|
print(f"💡 Your local IP: {local_ip}")
|
|
print(f" Access from phone: https://{local_ip}:{PORT}")
|
|
print()
|
|
|
|
print("🔄 Starting HTTPS server...")
|
|
print()
|
|
|
|
try:
|
|
# Run with ad-hoc SSL context (self-signed certificate)
|
|
# Flask will automatically generate a certificate
|
|
app.run(
|
|
host=HOST,
|
|
port=PORT,
|
|
debug=DEBUG,
|
|
ssl_context='adhoc' # Auto-generate self-signed cert
|
|
)
|
|
except OSError as e:
|
|
if "Address already in use" in str(e):
|
|
print(f"❌ ERROR: Port {PORT} is already in use!")
|
|
print(f" Try a different port or stop the other service.")
|
|
sys.exit(1)
|
|
else:
|
|
raise
|
|
except KeyboardInterrupt:
|
|
print("\n\n👋 Server stopped by user")
|
|
sys.exit(0)
|