Added HTTP-API support, Standalone AP Support and Built-in Melodies
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════════════════════════════
|
||||
* 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);
|
||||
}
|
||||
Reference in New Issue
Block a user