feature/capabilities (#1)

Reviewed-on: #1
This commit is contained in:
2025-08-28 11:17:24 +02:00
parent a9fb4252da
commit d7e98a41fa
3 changed files with 127 additions and 3 deletions

View File

@@ -27,6 +27,34 @@ void ApiServer::addEndpoint(const String& uri, int method, std::function<void(As
server.on(uri.c_str(), method, requestHandler, uploadHandler);
}
// Overloads that also record minimal capability specs
void ApiServer::addEndpoint(const String& uri, int method, std::function<void(AsyncWebServerRequest*)> requestHandler,
const std::vector<ParamSpec>& params) {
capabilityRegistry.push_back(EndpointCapability{uri, method, params});
serviceRegistry.push_back(std::make_tuple(uri, method));
if (ctx.memberList && !ctx.memberList->empty()) {
auto it = ctx.memberList->find(ctx.hostname);
if (it != ctx.memberList->end()) {
it->second.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,
const std::vector<ParamSpec>& params) {
capabilityRegistry.push_back(EndpointCapability{uri, method, params});
serviceRegistry.push_back(std::make_tuple(uri, method));
if (ctx.memberList && !ctx.memberList->empty()) {
auto it = ctx.memberList->find(ctx.hostname);
if (it != ctx.memberList->end()) {
it->second.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));
@@ -38,12 +66,21 @@ void ApiServer::begin() {
);
addEndpoint("/api/node/restart", HTTP_POST,
std::bind(&ApiServer::onRestartRequest, this, std::placeholders::_1));
// New: Capabilities endpoint
addEndpoint("/api/capabilities", HTTP_GET,
std::bind(&ApiServer::onCapabilitiesRequest, this, std::placeholders::_1));
// Task management endpoints
addEndpoint("/api/tasks/status", HTTP_GET,
std::bind(&ApiServer::onTaskStatusRequest, this, std::placeholders::_1));
addEndpoint("/api/tasks/control", HTTP_POST,
std::bind(&ApiServer::onTaskControlRequest, this, std::placeholders::_1));
std::bind(&ApiServer::onTaskControlRequest, this, std::placeholders::_1),
std::vector<ParamSpec>{
ParamSpec{String("task"), true, String("body"), String("string"), {}},
ParamSpec{String("action"), true, String("body"), String("string"), {String("enable"), String("disable"), String("start"), String("stop"), String("status")}}
}
);
server.begin();
}
@@ -296,4 +333,68 @@ void ApiServer::onTaskControlRequest(AsyncWebServerRequest *request) {
serializeJson(doc, json);
request->send(400, "application/json", json);
}
}
void ApiServer::onCapabilitiesRequest(AsyncWebServerRequest *request) {
JsonDocument doc;
JsonArray endpointsArr = doc["endpoints"].to<JsonArray>();
// Track seen (uri|method) to avoid duplicates
std::vector<String> seen;
auto makeKey = [](const String& uri, int method) {
String k = uri; k += "|"; k += method; return k;
};
auto methodStrFromInt = [](int method) -> const char* {
switch (method) {
case HTTP_GET: return "GET";
case HTTP_POST: return "POST";
case HTTP_PUT: return "PUT";
case HTTP_DELETE: return "DELETE";
case HTTP_PATCH: return "PATCH";
default: return "UNKNOWN";
}
};
// Rich entries first
for (const auto& cap : capabilityRegistry) {
String key = makeKey(cap.uri, cap.method);
seen.push_back(key);
JsonObject obj = endpointsArr.add<JsonObject>();
obj["uri"] = cap.uri;
obj["method"] = methodStrFromInt(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);
}
}
}
}
}
// Then any endpoints without explicit param specs
for (const auto& entry : serviceRegistry) {
const String& uri = std::get<0>(entry);
int method = std::get<1>(entry);
String key = makeKey(uri, method);
bool exists = false;
for (const auto& s : seen) { if (s == key) { exists = true; break; } }
if (!exists) {
JsonObject obj = endpointsArr.add<JsonObject>();
obj["uri"] = uri;
obj["method"] = methodStrFromInt(method);
}
}
String json;
serializeJson(doc, json);
request->send(200, "application/json", json);
}