feat: GET node config endpoint
This commit is contained in:
111
ctl.sh
111
ctl.sh
@@ -17,6 +17,7 @@ source .env
|
|||||||
## node wifi <ssid> <password> [ip] - Configure WiFi on node
|
## node wifi <ssid> <password> [ip] - Configure WiFi on node
|
||||||
## node label set <key=value> [ip] - Set a label on node
|
## node label set <key=value> [ip] - Set a label on node
|
||||||
## node label delete <key> [ip] - Delete a label from 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
|
## node status [ip] - Get node status and information
|
||||||
## monitor - Monitor serial output
|
## monitor - Monitor serial output
|
||||||
##
|
##
|
||||||
@@ -28,6 +29,8 @@ source .env
|
|||||||
## ./ctl.sh node label set "environment=production"
|
## ./ctl.sh node label set "environment=production"
|
||||||
## ./ctl.sh node label set "location=office" 192.168.1.100
|
## ./ctl.sh node label set "location=office" 192.168.1.100
|
||||||
## ./ctl.sh node label delete "environment"
|
## ./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
|
||||||
## ./ctl.sh node status 192.168.1.100
|
## ./ctl.sh node status 192.168.1.100
|
||||||
|
|
||||||
@@ -316,6 +319,114 @@ function node {
|
|||||||
${@:-info}
|
${@:-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 {
|
function status {
|
||||||
local node_ip="${1:-$API_NODE}"
|
local node_ip="${1:-$API_NODE}"
|
||||||
|
|
||||||
|
|||||||
@@ -21,4 +21,5 @@ private:
|
|||||||
void handleRestartRequest(AsyncWebServerRequest* request);
|
void handleRestartRequest(AsyncWebServerRequest* request);
|
||||||
void handleEndpointsRequest(AsyncWebServerRequest* request);
|
void handleEndpointsRequest(AsyncWebServerRequest* request);
|
||||||
void handleConfigRequest(AsyncWebServerRequest* request);
|
void handleConfigRequest(AsyncWebServerRequest* request);
|
||||||
|
void handleGetConfigRequest(AsyncWebServerRequest* request);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ void NodeService::registerEndpoints(ApiServer& api) {
|
|||||||
ParamSpec{String("labels"), true, String("body"), String("json"), {}, String("")}
|
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
|
// Generic local event endpoint
|
||||||
api.registerEndpoint("/api/node/event", HTTP_POST,
|
api.registerEndpoint("/api/node/event", HTTP_POST,
|
||||||
[this](AsyncWebServerRequest* request) {
|
[this](AsyncWebServerRequest* request) {
|
||||||
@@ -233,3 +238,60 @@ void NodeService::handleConfigRequest(AsyncWebServerRequest* request) {
|
|||||||
request->send(500, "application/json", "{\"error\":\"Failed to save configuration\"}");
|
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);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user