188 lines
6.7 KiB
C++
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);
|
|
}
|