#!/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/", methods=["GET"]) def get_station_details(mac): """ GET /api/v1/sta/details/ 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/": "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)