feat: GET node config endpoint

This commit is contained in:
2025-10-15 22:36:20 +02:00
parent 7063b1ab16
commit b404852fc7
3 changed files with 174 additions and 0 deletions

111
ctl.sh
View File

@@ -17,6 +17,7 @@ source .env
## node wifi <ssid> <password> [ip] - Configure WiFi on node
## node label set <key=value> [ip] - Set a label on node
## node label delete <key> [ip] - Delete a label from node
## node config get [ip] - Get node configuration
## node status [ip] - Get node status and information
## monitor - Monitor serial output
##
@@ -28,6 +29,8 @@ source .env
## ./ctl.sh node label set "environment=production"
## ./ctl.sh node label set "location=office" 192.168.1.100
## ./ctl.sh node label delete "environment"
## ./ctl.sh node config get
## ./ctl.sh node config get 192.168.1.100
## ./ctl.sh node status
## ./ctl.sh node status 192.168.1.100
@@ -316,6 +319,114 @@ function node {
${@:-info}
}
function config {
function get {
local node_ip="${1:-$API_NODE}"
echo "Getting configuration for node $node_ip..."
# Get node configuration
response=$(curl -s -w "\n%{http_code}" "http://$node_ip/api/node/config" 2>/dev/null || echo -e "\n000")
# Extract HTTP status code and response body
http_code=$(echo "$response" | tail -n1)
response_body=$(echo "$response" | head -n -1)
# Check if curl succeeded
if [ "$http_code" = "000" ] || [ -z "$response_body" ]; then
echo "Error: Failed to connect to node at $node_ip"
echo "Please check:"
echo " - Node is powered on and connected to network"
echo " - IP address is correct"
echo " - Node is running Spore firmware"
return 1
fi
# Check HTTP status code
if [ "$http_code" != "200" ]; then
echo "Error: HTTP $http_code - Server error"
echo "Response: $response_body"
return 1
fi
# Parse and display the response in a nice format
echo ""
echo "=== Node Configuration ==="
echo "Node IP: $node_ip"
echo "Retrieved at: $(date)"
echo ""
# WiFi Configuration
echo "=== WiFi Configuration ==="
echo "SSID: $(echo "$response_body" | jq -r '.wifi.ssid // "N/A"')"
echo "Connect Timeout: $(echo "$response_body" | jq -r '.wifi.connect_timeout_ms // "N/A"') ms"
echo "Retry Delay: $(echo "$response_body" | jq -r '.wifi.retry_delay_ms // "N/A"') ms"
echo "Password: [HIDDEN]"
echo ""
# Network Configuration
echo "=== Network Configuration ==="
echo "UDP Port: $(echo "$response_body" | jq -r '.network.udp_port // "N/A"')"
echo "API Server Port: $(echo "$response_body" | jq -r '.network.api_server_port // "N/A"')"
echo ""
# Cluster Configuration
echo "=== Cluster Configuration ==="
echo "Discovery Interval: $(echo "$response_body" | jq -r '.cluster.discovery_interval_ms // "N/A"') ms"
echo "Heartbeat Interval: $(echo "$response_body" | jq -r '.cluster.heartbeat_interval_ms // "N/A"') ms"
echo "Cluster Listen Interval: $(echo "$response_body" | jq -r '.cluster.cluster_listen_interval_ms // "N/A"') ms"
echo "Status Update Interval: $(echo "$response_body" | jq -r '.cluster.status_update_interval_ms // "N/A"') ms"
echo "Member Info Update Interval: $(echo "$response_body" | jq -r '.cluster.member_info_update_interval_ms // "N/A"') ms"
echo "Print Interval: $(echo "$response_body" | jq -r '.cluster.print_interval_ms // "N/A"') ms"
echo ""
# Node Status Thresholds
echo "=== Node Status Thresholds ==="
echo "Active Threshold: $(echo "$response_body" | jq -r '.thresholds.node_active_threshold_ms // "N/A"') ms"
echo "Inactive Threshold: $(echo "$response_body" | jq -r '.thresholds.node_inactive_threshold_ms // "N/A"') ms"
echo "Dead Threshold: $(echo "$response_body" | jq -r '.thresholds.node_dead_threshold_ms // "N/A"') ms"
echo ""
# System Configuration
echo "=== System Configuration ==="
echo "Restart Delay: $(echo "$response_body" | jq -r '.system.restart_delay_ms // "N/A"') ms"
echo "JSON Doc Size: $(echo "$response_body" | jq -r '.system.json_doc_size // "N/A"') bytes"
echo ""
# Memory Management
echo "=== Memory Management ==="
echo "Low Memory Threshold: $(echo "$response_body" | jq -r '.memory.low_memory_threshold_bytes // "N/A"') bytes"
echo "Critical Memory Threshold: $(echo "$response_body" | jq -r '.memory.critical_memory_threshold_bytes // "N/A"') bytes"
echo "Max Concurrent HTTP Requests: $(echo "$response_body" | jq -r '.memory.max_concurrent_http_requests // "N/A"')"
echo ""
# Custom Labels
labels=$(echo "$response_body" | jq -r '.labels // {}')
if [ "$labels" != "{}" ] && [ "$labels" != "null" ]; then
echo "=== Custom Labels ==="
echo "$labels" | jq -r 'to_entries[] | "\(.key): \(.value)"'
echo ""
else
echo "=== Custom Labels ==="
echo "No custom labels set"
echo ""
fi
# Metadata
echo "=== Metadata ==="
echo "Configuration Version: $(echo "$response_body" | jq -r '.version // "N/A"')"
echo "Retrieved Timestamp: $(echo "$response_body" | jq -r '.retrieved_at // "N/A"')"
echo ""
echo "=== Raw JSON Response ==="
echo "$response_body" | jq '.'
return 0
}
${@:-info}
}
function status {
local node_ip="${1:-$API_NODE}"

View File

@@ -21,4 +21,5 @@ private:
void handleRestartRequest(AsyncWebServerRequest* request);
void handleEndpointsRequest(AsyncWebServerRequest* request);
void handleConfigRequest(AsyncWebServerRequest* request);
void handleGetConfigRequest(AsyncWebServerRequest* request);
};

View File

@@ -37,6 +37,11 @@ void NodeService::registerEndpoints(ApiServer& api) {
ParamSpec{String("labels"), true, String("body"), String("json"), {}, String("")}
});
// Config endpoint for getting node configuration (without WiFi password)
api.registerEndpoint("/api/node/config", HTTP_GET,
[this](AsyncWebServerRequest* request) { handleGetConfigRequest(request); },
std::vector<ParamSpec>{});
// Generic local event endpoint
api.registerEndpoint("/api/node/event", HTTP_POST,
[this](AsyncWebServerRequest* request) {
@@ -233,3 +238,60 @@ void NodeService::handleConfigRequest(AsyncWebServerRequest* request) {
request->send(500, "application/json", "{\"error\":\"Failed to save configuration\"}");
}
}
void NodeService::handleGetConfigRequest(AsyncWebServerRequest* request) {
JsonDocument doc;
// WiFi Configuration (excluding password for security)
JsonObject wifiObj = doc["wifi"].to<JsonObject>();
wifiObj["ssid"] = ctx.config.wifi_ssid;
wifiObj["connect_timeout_ms"] = ctx.config.wifi_connect_timeout_ms;
wifiObj["retry_delay_ms"] = ctx.config.wifi_retry_delay_ms;
// Network Configuration
JsonObject networkObj = doc["network"].to<JsonObject>();
networkObj["udp_port"] = ctx.config.udp_port;
networkObj["api_server_port"] = ctx.config.api_server_port;
// Cluster Configuration
JsonObject clusterObj = doc["cluster"].to<JsonObject>();
clusterObj["discovery_interval_ms"] = ctx.config.discovery_interval_ms;
clusterObj["heartbeat_interval_ms"] = ctx.config.heartbeat_interval_ms;
clusterObj["cluster_listen_interval_ms"] = ctx.config.cluster_listen_interval_ms;
clusterObj["status_update_interval_ms"] = ctx.config.status_update_interval_ms;
clusterObj["member_info_update_interval_ms"] = ctx.config.member_info_update_interval_ms;
clusterObj["print_interval_ms"] = ctx.config.print_interval_ms;
// Node Status Thresholds
JsonObject thresholdsObj = doc["thresholds"].to<JsonObject>();
thresholdsObj["node_active_threshold_ms"] = ctx.config.node_active_threshold_ms;
thresholdsObj["node_inactive_threshold_ms"] = ctx.config.node_inactive_threshold_ms;
thresholdsObj["node_dead_threshold_ms"] = ctx.config.node_dead_threshold_ms;
// System Configuration
JsonObject systemObj = doc["system"].to<JsonObject>();
systemObj["restart_delay_ms"] = ctx.config.restart_delay_ms;
systemObj["json_doc_size"] = ctx.config.json_doc_size;
// Memory Management
JsonObject memoryObj = doc["memory"].to<JsonObject>();
memoryObj["low_memory_threshold_bytes"] = ctx.config.low_memory_threshold_bytes;
memoryObj["critical_memory_threshold_bytes"] = ctx.config.critical_memory_threshold_bytes;
memoryObj["max_concurrent_http_requests"] = ctx.config.max_concurrent_http_requests;
// Custom Labels
if (!ctx.config.labels.empty()) {
JsonObject labelsObj = doc["labels"].to<JsonObject>();
for (const auto& kv : ctx.config.labels) {
labelsObj[kv.first.c_str()] = kv.second;
}
}
// Add metadata
doc["version"] = "1.0";
doc["retrieved_at"] = millis();
String json;
serializeJson(doc, json);
request->send(200, "application/json", json);
}