refactor: service folder structure

This commit is contained in:
2025-09-13 13:11:32 +02:00
parent 00aff9df3e
commit 2971d863d0
19 changed files with 43 additions and 34 deletions

View File

@@ -0,0 +1,157 @@
#include "services/NodeService.h"
#include "ApiServer.h"
NodeService::NodeService(NodeContext& ctx) : ctx(ctx) {}
void NodeService::registerEndpoints(ApiServer& api) {
// Status endpoint
api.addEndpoint("/api/node/status", HTTP_GET,
[this](AsyncWebServerRequest* request) { handleStatusRequest(request); },
std::vector<ParamSpec>{});
// Update endpoint with file upload
api.addEndpoint("/api/node/update", HTTP_POST,
[this](AsyncWebServerRequest* request) { handleUpdateRequest(request); },
[this](AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final) {
handleUpdateUpload(request, filename, index, data, len, final);
},
std::vector<ParamSpec>{
ParamSpec{String("firmware"), true, String("body"), String("file"), {}, String("")}
});
// Restart endpoint
api.addEndpoint("/api/node/restart", HTTP_POST,
[this](AsyncWebServerRequest* request) { handleRestartRequest(request); },
std::vector<ParamSpec>{});
// Capabilities endpoint
api.addEndpoint("/api/node/capabilities", HTTP_GET,
[this](AsyncWebServerRequest* request) { handleCapabilitiesRequest(request); },
std::vector<ParamSpec>{});
}
void NodeService::handleStatusRequest(AsyncWebServerRequest* request) {
JsonDocument doc;
doc["freeHeap"] = ESP.getFreeHeap();
doc["chipId"] = ESP.getChipId();
doc["sdkVersion"] = ESP.getSdkVersion();
doc["cpuFreqMHz"] = ESP.getCpuFreqMHz();
doc["flashChipSize"] = ESP.getFlashChipSize();
// Include local node labels if present
if (ctx.memberList) {
auto it = ctx.memberList->find(ctx.hostname);
if (it != ctx.memberList->end()) {
JsonObject labelsObj = doc["labels"].to<JsonObject>();
for (const auto& kv : it->second.labels) {
labelsObj[kv.first.c_str()] = kv.second;
}
} else if (!ctx.self.labels.empty()) {
JsonObject labelsObj = doc["labels"].to<JsonObject>();
for (const auto& kv : ctx.self.labels) {
labelsObj[kv.first.c_str()] = kv.second;
}
}
}
String json;
serializeJson(doc, json);
request->send(200, "application/json", json);
}
void NodeService::handleUpdateRequest(AsyncWebServerRequest* request) {
bool success = !Update.hasError();
AsyncWebServerResponse* response = request->beginResponse(200, "application/json",
success ? "{\"status\": \"OK\"}" : "{\"status\": \"FAIL\"}");
response->addHeader("Connection", "close");
request->send(response);
request->onDisconnect([]() {
Serial.println("[API] Restart device");
delay(10);
ESP.restart();
});
}
void NodeService::handleUpdateUpload(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;
}
}
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);
}
}
}
void NodeService::handleRestartRequest(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();
});
}
void NodeService::handleCapabilitiesRequest(AsyncWebServerRequest* request) {
JsonDocument doc;
JsonArray endpointsArr = doc["endpoints"].to<JsonArray>();
// Add all registered capabilities
for (const auto& cap : ctx.capabilities) {
JsonObject obj = endpointsArr.add<JsonObject>();
obj["uri"] = cap.uri;
obj["method"] = ApiServer::methodToStr(cap.method);
if (!cap.params.empty()) {
JsonArray paramsArr = obj["params"].to<JsonArray>();
for (const auto& ps : cap.params) {
JsonObject p = paramsArr.add<JsonObject>();
p["name"] = ps.name;
p["location"] = ps.location;
p["required"] = ps.required;
p["type"] = ps.type;
if (!ps.values.empty()) {
JsonArray allowed = p["values"].to<JsonArray>();
for (const auto& v : ps.values) {
allowed.add(v);
}
}
if (ps.defaultValue.length() > 0) {
p["default"] = ps.defaultValue;
}
}
}
}
String json;
serializeJson(doc, json);
request->send(200, "application/json", json);
}