feat: add geo metadata to nodes and dashboard

- Extend DashboardNodeHealth and NodeItem interfaces to include geo_name, geo_latitude, and geo_longitude.
- Update NodeDetailPage and NodesPage components to handle geo metadata in forms and payloads.
- Implement validation for geo fields in NodesPage using Zod.
- Enhance dashboard overview tests to include geo metadata for nodes.
- Create WorldStage component to visualize node health and geo locations on a world map.
- Update design and development documentation to reflect the addition of geo metadata for nodes.
This commit is contained in:
ryan
2026-03-14 12:59:22 +08:00
parent b991e2e635
commit fc3db065db
16 changed files with 1188 additions and 238 deletions
+14 -2
View File
@@ -184,13 +184,19 @@ func TestPhase2AgentLifecycle(t *testing.T) {
}
createdNodeResp := performJSONRequest(t, engine, adminToken, http.MethodPost, "/api/nodes/", map[string]any{
"name": "shanghai-edge-1",
"name": "shanghai-edge-1",
"geo_name": "Shanghai",
"geo_latitude": 31.2304,
"geo_longitude": 121.4737,
})
var createdNode service.NodeView
decodeResponseData(t, createdNodeResp, &createdNode)
if createdNode.AgentToken == "" || createdNode.Status != service.NodeStatusPending {
t.Fatal("expected created node to expose agent token with pending status")
}
if createdNode.GeoName != "Shanghai" || createdNode.GeoLatitude == nil || createdNode.GeoLongitude == nil {
t.Fatalf("expected created node to expose geo metadata, got %+v", createdNode)
}
heartbeatPayload := map[string]any{
"node_id": "spoofed-node-id",
@@ -324,12 +330,18 @@ func TestPhase2AgentLifecycle(t *testing.T) {
}
updatedNodeResp := performJSONRequest(t, engine, adminToken, http.MethodPut, "/api/nodes/"+toString(createdNode.ID), map[string]any{
"name": "shanghai-edge-1-renamed",
"name": "shanghai-edge-1-renamed",
"geo_name": "Tokyo",
"geo_latitude": 35.6762,
"geo_longitude": 139.6503,
})
decodeResponseData(t, updatedNodeResp, &createdNode)
if createdNode.Name != "shanghai-edge-1-renamed" {
t.Fatal("expected node name to be editable")
}
if createdNode.GeoName != "Tokyo" || createdNode.GeoLatitude == nil || createdNode.GeoLongitude == nil {
t.Fatalf("expected node geo metadata to be editable, got %+v", createdNode)
}
oldTime := time.Now().Add(-common.NodeOfflineThreshold - time.Minute)
if err := model.DB.Model(&model.Node{}).Where("node_id = ?", createdNode.NodeID).Update("last_seen_at", oldTime).Error; err != nil {