/* * ═══════════════════════════════════════════════════════════════════════════════════ * HTTPREQUESTHANDLER.CPP - HTTP REST API Request Handler Implementation * ═══════════════════════════════════════════════════════════════════════════════════ */ #include "HTTPRequestHandler.hpp" #include "../CommandHandler/CommandHandler.hpp" #include "../../ConfigManager/ConfigManager.hpp" #include "../../Logging/Logging.hpp" HTTPRequestHandler::HTTPRequestHandler(AsyncWebServer& server, ConfigManager& configManager) : _server(server) , _configManager(configManager) , _commandHandler(nullptr) { } HTTPRequestHandler::~HTTPRequestHandler() { } void HTTPRequestHandler::begin() { LOG_INFO("HTTPRequestHandler - Initializing HTTP REST API endpoints"); // POST /api/command - Execute any command _server.on("/api/command", HTTP_POST, [](AsyncWebServerRequest* request) { // This is called when request is complete but body is empty request->send(400, "application/json", "{\"error\":\"No body provided\"}"); }, nullptr, // No file upload handler [this](AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) { // This is called for body data if (index == 0) { // First chunk - could allocate buffers if needed } if (index + len == total) { // Last chunk - process the complete request handleCommandRequest(request, data, len); } } ); // GET /api/status - Get system status _server.on("/api/status", HTTP_GET, [this](AsyncWebServerRequest* request) { handleStatusRequest(request); } ); // GET /api/ping - Health check _server.on("/api/ping", HTTP_GET, [this](AsyncWebServerRequest* request) { handlePingRequest(request); } ); // Enable CORS for API endpoints (allows web apps to call the API) DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Content-Type"); LOG_INFO("HTTPRequestHandler - REST API endpoints registered"); LOG_INFO(" POST /api/command - Execute commands"); LOG_INFO(" GET /api/status - System status"); LOG_INFO(" GET /api/ping - Health check"); } void HTTPRequestHandler::setCommandHandlerReference(CommandHandler* handler) { _commandHandler = handler; LOG_DEBUG("HTTPRequestHandler - CommandHandler reference set"); } bool HTTPRequestHandler::isHealthy() const { // HTTP handler is healthy if it has been initialized with dependencies return _commandHandler != nullptr; } void HTTPRequestHandler::handleCommandRequest(AsyncWebServerRequest* request, uint8_t* data, size_t len) { if (!_commandHandler) { sendErrorResponse(request, 503, "Command handler not initialized"); return; } // Parse JSON from body JsonDocument doc; DeserializationError error = deserializeJson(doc, data, len); if (error) { LOG_WARNING("HTTPRequestHandler - JSON parse error: %s", error.c_str()); sendErrorResponse(request, 400, "Invalid JSON"); return; } LOG_DEBUG("HTTPRequestHandler - Processing command via HTTP"); // Create message context for HTTP (treat as WebSocket with special ID) CommandHandler::MessageContext context(CommandHandler::MessageSource::WEBSOCKET, 0xFFFFFFFF); // Capture request pointer for response AsyncWebServerRequest* capturedRequest = request; bool responseSent = false; // Set temporary response callback to capture the response auto originalCallback = [capturedRequest, &responseSent](const String& response, const CommandHandler::MessageContext& ctx) { if (!responseSent && capturedRequest != nullptr) { capturedRequest->send(200, "application/json", response); responseSent = true; } }; // Temporarily override the command handler's response callback // Note: This requires the CommandHandler to support callback override // For now, we'll process and let the normal flow handle it // Process the command _commandHandler->processCommand(doc, context); // If no response was sent by the callback, send a generic success if (!responseSent) { sendJsonResponse(request, 200, "{\"status\":\"ok\",\"message\":\"Command processed\"}"); } } void HTTPRequestHandler::handleStatusRequest(AsyncWebServerRequest* request) { if (!_commandHandler) { sendErrorResponse(request, 503, "Command handler not initialized"); return; } LOG_DEBUG("HTTPRequestHandler - Status request via HTTP"); // Create a status command JsonDocument doc; doc["group"] = "system"; doc["action"] = "status"; // Create message context CommandHandler::MessageContext context(CommandHandler::MessageSource::WEBSOCKET, 0xFFFFFFFF); // Capture request for response AsyncWebServerRequest* capturedRequest = request; bool responseSent = false; // Process via command handler _commandHandler->processCommand(doc, context); // Fallback response if needed if (!responseSent) { JsonDocument response; response["status"] = "ok"; response["device_uid"] = _configManager.getDeviceUID(); response["fw_version"] = _configManager.getFwVersion(); String output; serializeJson(response, output); sendJsonResponse(request, 200, output); } } void HTTPRequestHandler::handlePingRequest(AsyncWebServerRequest* request) { LOG_DEBUG("HTTPRequestHandler - Ping request via HTTP"); JsonDocument response; response["status"] = "ok"; response["message"] = "pong"; response["uptime"] = millis(); String output; serializeJson(response, output); sendJsonResponse(request, 200, output); } void HTTPRequestHandler::sendJsonResponse(AsyncWebServerRequest* request, int code, const String& json) { request->send(code, "application/json", json); } void HTTPRequestHandler::sendErrorResponse(AsyncWebServerRequest* request, int code, const String& error) { JsonDocument doc; doc["status"] = "error"; doc["error"] = error; String output; serializeJson(doc, output); sendJsonResponse(request, code, output); }