chore: refactoring and docs
This commit is contained in:
169
src/ApiServer.cpp
Normal file
169
src/ApiServer.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
#include "ApiServer.h"
|
||||
|
||||
ApiServer::ApiServer(NodeContext& ctx, uint16_t port) : server(port), ctx(ctx) {}
|
||||
|
||||
void ApiServer::addEndpoint(const String& uri, int method, std::function<void(AsyncWebServerRequest*)> requestHandler) {
|
||||
serviceRegistry.push_back(std::make_tuple(uri, method));
|
||||
// Store in NodeInfo for local node
|
||||
if (ctx.memberList && !ctx.memberList->empty()) {
|
||||
(*ctx.memberList)[0].apiEndpoints.push_back(std::make_tuple(uri, method));
|
||||
}
|
||||
server.on(uri.c_str(), method, requestHandler);
|
||||
}
|
||||
|
||||
void ApiServer::addEndpoint(const String& uri, int method, std::function<void(AsyncWebServerRequest*)> requestHandler,
|
||||
std::function<void(AsyncWebServerRequest*, const String&, size_t, uint8_t*, size_t, bool)> uploadHandler) {
|
||||
serviceRegistry.push_back(std::make_tuple(uri, method));
|
||||
if (ctx.memberList && !ctx.memberList->empty()) {
|
||||
(*ctx.memberList)[0].apiEndpoints.push_back(std::make_tuple(uri, method));
|
||||
}
|
||||
server.on(uri.c_str(), method, requestHandler, uploadHandler);
|
||||
}
|
||||
|
||||
void ApiServer::begin() {
|
||||
addEndpoint("/api/node/status", HTTP_GET,
|
||||
std::bind(&ApiServer::onSystemStatusRequest, this, std::placeholders::_1));
|
||||
addEndpoint("/api/cluster/members", HTTP_GET,
|
||||
std::bind(&ApiServer::onClusterMembersRequest, this, std::placeholders::_1));
|
||||
addEndpoint("/api/node/update", HTTP_POST,
|
||||
std::bind(&ApiServer::onFirmwareUpdateRequest, this, std::placeholders::_1),
|
||||
std::bind(&ApiServer::onFirmwareUpload, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6)
|
||||
);
|
||||
addEndpoint("/api/node/restart", HTTP_POST,
|
||||
std::bind(&ApiServer::onRestartRequest, this, std::placeholders::_1));
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void ApiServer::onSystemStatusRequest(AsyncWebServerRequest *request) {
|
||||
JsonDocument doc;
|
||||
doc["freeHeap"] = ESP.getFreeHeap();
|
||||
doc["chipId"] = ESP.getChipId();
|
||||
doc["sdkVersion"] = ESP.getSdkVersion();
|
||||
doc["cpuFreqMHz"] = ESP.getCpuFreqMHz();
|
||||
doc["flashChipSize"] = ESP.getFlashChipSize();
|
||||
JsonArray apiArr = doc["api"].to<JsonArray>();
|
||||
for (const auto& entry : serviceRegistry) {
|
||||
JsonObject apiObj = apiArr.add<JsonObject>();
|
||||
apiObj["uri"] = std::get<0>(entry);
|
||||
apiObj["method"] = std::get<1>(entry);
|
||||
}
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
request->send(200, "application/json", json);
|
||||
}
|
||||
|
||||
void ApiServer::onClusterMembersRequest(AsyncWebServerRequest *request) {
|
||||
JsonDocument doc;
|
||||
JsonArray arr = doc["members"].to<JsonArray>();
|
||||
for (const auto& node : *ctx.memberList) {
|
||||
JsonObject obj = arr.add<JsonObject>();
|
||||
obj["hostname"] = node.hostname;
|
||||
obj["ip"] = node.ip.toString();
|
||||
obj["lastSeen"] = node.lastSeen;
|
||||
obj["latency"] = node.latency;
|
||||
obj["status"] = statusToStr(node.status);
|
||||
obj["resources"]["freeHeap"] = node.resources.freeHeap;
|
||||
obj["resources"]["chipId"] = node.resources.chipId;
|
||||
obj["resources"]["sdkVersion"] = node.resources.sdkVersion;
|
||||
obj["resources"]["cpuFreqMHz"] = node.resources.cpuFreqMHz;
|
||||
obj["resources"]["flashChipSize"] = node.resources.flashChipSize;
|
||||
JsonArray apiArr = obj["api"].to<JsonArray>();
|
||||
for (const auto& endpoint : node.apiEndpoints) {
|
||||
JsonObject apiObj = apiArr.add<JsonObject>();
|
||||
apiObj["uri"] = std::get<0>(endpoint);
|
||||
methodToStr(endpoint, apiObj);
|
||||
}
|
||||
}
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
request->send(200, "application/json", json);
|
||||
}
|
||||
|
||||
void ApiServer::methodToStr(const std::tuple<String, int> &endpoint, ArduinoJson::V742PB22::JsonObject &apiObj)
|
||||
{
|
||||
int method = std::get<1>(endpoint);
|
||||
const char *methodStr = nullptr;
|
||||
switch (method)
|
||||
{
|
||||
case HTTP_GET:
|
||||
methodStr = "GET";
|
||||
break;
|
||||
case HTTP_POST:
|
||||
methodStr = "POST";
|
||||
break;
|
||||
case HTTP_PUT:
|
||||
methodStr = "PUT";
|
||||
break;
|
||||
case HTTP_DELETE:
|
||||
methodStr = "DELETE";
|
||||
break;
|
||||
case HTTP_PATCH:
|
||||
methodStr = "PATCH";
|
||||
break;
|
||||
default:
|
||||
methodStr = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
apiObj["method"] = methodStr;
|
||||
}
|
||||
|
||||
void ApiServer::onFirmwareUpdateRequest(AsyncWebServerRequest *request) {
|
||||
bool hasError = !Update.hasError();
|
||||
AsyncWebServerResponse *response = request->beginResponse(200, "application/json", hasError ? "{\"status\": \"OK\"}" : "{\"status\": \"FAIL\"}");
|
||||
response->addHeader("Connection", "close");
|
||||
request->send(response);
|
||||
request->onDisconnect([]() {
|
||||
Serial.println("[API] Restart device");
|
||||
delay(10);
|
||||
ESP.restart();
|
||||
});
|
||||
}
|
||||
|
||||
void ApiServer::onFirmwareUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) {
|
||||
if (!index) {
|
||||
Serial.print("[OTA] Update Start ");
|
||||
Serial.println(filename);
|
||||
Update.runAsync(true);
|
||||
if(!Update.begin(request->contentLength(), U_FLASH)) {
|
||||
Serial.println("[OTA] Update failed: not enough space");
|
||||
Update.printError(Serial);
|
||||
AsyncWebServerResponse *response = request->beginResponse(500, "application/json", "{\"status\": \"FAIL\"}");
|
||||
response->addHeader("Connection", "close");
|
||||
request->send(response);
|
||||
return;
|
||||
} else {
|
||||
Update.printError(Serial);
|
||||
}
|
||||
}
|
||||
if (!Update.hasError()){
|
||||
if (Update.write(data, len) != len) {
|
||||
Update.printError(Serial);
|
||||
}
|
||||
}
|
||||
if (final) {
|
||||
if (Update.end(true)) {
|
||||
if(Update.isFinished()) {
|
||||
Serial.print("[OTA] Update Success with ");
|
||||
Serial.print(index + len);
|
||||
Serial.println("B");
|
||||
} else {
|
||||
Serial.println("[OTA] Update not finished");
|
||||
}
|
||||
} else {
|
||||
Serial.print("[OTA] Update failed: ");
|
||||
Update.printError(Serial);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void ApiServer::onRestartRequest(AsyncWebServerRequest *request) {
|
||||
AsyncWebServerResponse *response = request->beginResponse(200, "application/json", "{\"status\": \"restarting\"}");
|
||||
response->addHeader("Connection", "close");
|
||||
request->send(response);
|
||||
request->onDisconnect([]() {
|
||||
Serial.println("[API] Restart device");
|
||||
delay(10);
|
||||
ESP.restart();
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user