Files
project-vesper/vesper/src/Communication/HTTPRequestHandler/HTTPRequestHandler.cpp

188 lines
6.7 KiB
C++

/*
* ═══════════════════════════════════════════════════════════════════════════════════
* 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);
}