Initial commit
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone Build is passing

This commit is contained in:
Juan José Gutiérrez de Quevedo Pérez 2026-02-04 14:14:28 +01:00
commit 94d8e201f5
10 changed files with 909 additions and 0 deletions

133
app.py Normal file
View file

@ -0,0 +1,133 @@
#!/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)