133 lines
4 KiB
Python
133 lines
4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Hostapd Web UI - Flask Application
|
|
Provides REST API endpoints for managing WiFi stations via hostapd.
|
|
"""
|
|
|
|
from flask import Flask, jsonify, request, render_template
|
|
from hostapd_client import HostapdClient, HostapdClientError
|
|
import logging
|
|
|
|
# Configure logging
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Initialize Flask app
|
|
app = Flask(__name__, template_folder="templates", static_folder="static")
|
|
|
|
# Initialize hostapd client
|
|
# TODO: Make interface configurable via environment variable or config file
|
|
hostapd_client = HostapdClient(interface="wlan0")
|
|
|
|
|
|
@app.route("/api/v1/sta/list", methods=["GET"])
|
|
def list_stations():
|
|
"""
|
|
GET /api/v1/sta/list
|
|
Returns JSON array of connected station MAC addresses
|
|
|
|
Returns:
|
|
JSON response with array of MAC addresses or error
|
|
"""
|
|
try:
|
|
stations = hostapd_client.list_stations()
|
|
logger.info(f"Retrieved {len(stations)} connected stations")
|
|
return jsonify(stations), 200
|
|
except HostapdClientError as e:
|
|
logger.error(f"Failed to list stations: {str(e)}")
|
|
return jsonify({"error": f"Failed to retrieve station list: {str(e)}"}), 500
|
|
except Exception as e:
|
|
logger.error(f"Unexpected error listing stations: {str(e)}")
|
|
return jsonify({"error": "Internal server error"}), 500
|
|
|
|
|
|
@app.route("/api/v1/sta/details/<mac>", methods=["GET"])
|
|
def get_station_details(mac):
|
|
"""
|
|
GET /api/v1/sta/details/<mac>
|
|
Returns JSON object with detailed information about a specific station
|
|
|
|
Args:
|
|
mac (str): MAC address of the station
|
|
|
|
Returns:
|
|
JSON response with station details or error
|
|
"""
|
|
try:
|
|
details = hostapd_client.get_station_details(mac)
|
|
logger.info(f"Retrieved details for station {mac}")
|
|
return jsonify(details), 200
|
|
except HostapdClientError as e:
|
|
logger.error(f"Failed to get details for {mac}: {str(e)}")
|
|
if "Invalid MAC address" in str(e):
|
|
return jsonify({"error": f"Invalid MAC address format: {mac}"}), 400
|
|
elif "No details found" in str(e):
|
|
return jsonify({"error": f"Station not found: {mac}"}), 404
|
|
else:
|
|
return (
|
|
jsonify({"error": f"Failed to retrieve station details: {str(e)}"}),
|
|
500,
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Unexpected error getting details for {mac}: {str(e)}")
|
|
return jsonify({"error": "Internal server error"}), 500
|
|
|
|
|
|
# Error handlers
|
|
@app.errorhandler(404)
|
|
def not_found(error):
|
|
"""Handle 404 Not Found errors"""
|
|
return jsonify({"error": "Endpoint not found"}), 404
|
|
|
|
|
|
@app.errorhandler(500)
|
|
def internal_error(error):
|
|
"""Handle 500 Internal Server Error"""
|
|
return jsonify({"error": "Internal server error"}), 500
|
|
|
|
|
|
@app.errorhandler(Exception)
|
|
def handle_exception(e):
|
|
"""Handle unhandled exceptions"""
|
|
logger.error(f"Unhandled exception: {str(e)}")
|
|
return jsonify({"error": "Internal server error"}), 500
|
|
|
|
|
|
# Health check endpoint
|
|
@app.route("/health", methods=["GET"])
|
|
def health_check():
|
|
"""Health check endpoint"""
|
|
return jsonify({"status": "healthy", "service": "hostapd-webui"}), 200
|
|
|
|
|
|
# Serve the main web interface
|
|
@app.route("/", methods=["GET"])
|
|
def index():
|
|
"""Serve the main web interface"""
|
|
return render_template("index.html")
|
|
|
|
|
|
# API endpoint with documentation
|
|
@app.route("/api", methods=["GET"])
|
|
def api_docs():
|
|
"""API documentation endpoint"""
|
|
return (
|
|
jsonify(
|
|
{
|
|
"service": "hostapd-webui",
|
|
"version": "1.0.0",
|
|
"endpoints": {
|
|
"GET /api/v1/sta/list": "Get list of connected stations",
|
|
"GET /api/v1/sta/details/<mac>": "Get details for specific station",
|
|
"GET /health": "Health check",
|
|
},
|
|
}
|
|
),
|
|
200,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Run the Flask app
|
|
# TODO: Make host/port configurable via environment variables
|
|
app.run(host="0.0.0.0", port=5000, debug=False)
|