From 2f5feb2e0f953e2390e29fd9e72989d5d23e06be Mon Sep 17 00:00:00 2001 From: xmonader Date: Wed, 23 Apr 2025 17:07:37 +0200 Subject: [PATCH 1/3] add online filter --- node-registrar/client/node.go | 38 ++++++++++++++++++----- node-registrar/client/types.go | 1 + node-registrar/docs/docs.go | 39 +++++++++++++++++++----- node-registrar/docs/swagger.json | 37 +++++++++++++++++++--- node-registrar/docs/swagger.yaml | 26 ++++++++++++++-- node-registrar/pkg/db/models.go | 13 +++++--- node-registrar/pkg/db/nodes.go | 44 ++++++++++++++++++++++++++- node-registrar/pkg/server/handlers.go | 4 ++- 8 files changed, 172 insertions(+), 30 deletions(-) diff --git a/node-registrar/client/node.go b/node-registrar/client/node.go index 5a4c328..c767dd2 100644 --- a/node-registrar/client/node.go +++ b/node-registrar/client/node.go @@ -59,6 +59,8 @@ type nodeCfg struct { twinID uint64 status string healthy bool + online *bool + lastSeen *int64 Location Location Resources Resources Interfaces []Interface @@ -100,6 +102,18 @@ func ListNodesWithHealthy() ListNodeOpts { } } +func ListNodesWithOnline(online bool) ListNodeOpts { + return func(n *nodeCfg) { + n.online = &online + } +} + +func ListNodesWithLastSeen(minutes int64) ListNodeOpts { + return func(n *nodeCfg) { + n.lastSeen = &minutes + } +} + func ListNodesWithTwinID(id uint64) ListNodeOpts { return func(n *nodeCfg) { n.twinID = id @@ -495,13 +509,15 @@ func (c *RegistrarClient) parseUpdateNodeOpts(node Node, opts []UpdateNodeOpts) func parseListNodeOpts(opts []ListNodeOpts) map[string]any { cfg := nodeCfg{ - nodeID: 0, - twinID: 0, - farmID: 0, - status: "", - healthy: false, - size: 50, - page: 1, + nodeID: 0, + twinID: 0, + farmID: 0, + status: "", + healthy: false, + online: nil, + lastSeen: nil, + size: 50, + page: 1, } for _, opt := range opts { @@ -530,6 +546,14 @@ func parseListNodeOpts(opts []ListNodeOpts) map[string]any { data["healthy"] = cfg.healthy } + if cfg.online != nil { + data["online"] = *cfg.online + } + + if cfg.lastSeen != nil { + data["last_seen"] = *cfg.lastSeen + } + data["size"] = cfg.size data["page"] = cfg.page diff --git a/node-registrar/client/types.go b/node-registrar/client/types.go index 2007195..1680a6b 100644 --- a/node-registrar/client/types.go +++ b/node-registrar/client/types.go @@ -30,6 +30,7 @@ type Node struct { Virtualized bool `json:"virtualized"` SerialNumber string `json:"serial_number"` UptimeReports []UptimeReport `json:"uptime"` + LastSeen *time.Time `json:"last_seen"` Approved bool } diff --git a/node-registrar/docs/docs.go b/node-registrar/docs/docs.go index c46e734..83a8873 100644 --- a/node-registrar/docs/docs.go +++ b/node-registrar/docs/docs.go @@ -470,6 +470,18 @@ const docTemplate = `{ "name": "healthy", "in": "query" }, + { + "type": "boolean", + "description": "Filter by online status (true = online, false = offline)", + "name": "online", + "in": "query" + }, + { + "type": "integer", + "description": "Filter nodes last seen within this many minutes", + "name": "last_seen", + "in": "query" + }, { "type": "integer", "default": 1, @@ -592,7 +604,7 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "Node details", + "description": "Node details with last_seen information", "schema": { "$ref": "#/definitions/db.Node" } @@ -894,6 +906,11 @@ const docTemplate = `{ }, "db.Farm": { "type": "object", + "required": [ + "farm_name", + "stellar_address", + "twin_id" + ], "properties": { "created_at": { "type": "string" @@ -930,7 +947,10 @@ const docTemplate = `{ "type": "object", "properties": { "ips": { - "type": "string" + "type": "array", + "items": { + "type": "string" + } }, "mac": { "type": "string" @@ -976,6 +996,10 @@ const docTemplate = `{ "$ref": "#/definitions/db.Interface" } }, + "last_seen": { + "description": "Last time the node sent an uptime report", + "type": "string" + }, "location": { "$ref": "#/definitions/db.Location" }, @@ -1139,8 +1163,7 @@ const docTemplate = `{ "maxLength": 40 }, "stellar_address": { - "type": "string", - "maxLength": 56 + "type": "string" } } }, @@ -1200,12 +1223,12 @@ const docTemplate = `{ // SwaggerInfo holds exported Swagger Info so clients can modify it var SwaggerInfo = &swag.Spec{ - Version: "", + Version: "1.0", Host: "", - BasePath: "", + BasePath: "/v1", Schemes: []string{}, - Title: "", - Description: "", + Title: "Node Registrar API", + Description: "API for managing TFGrid node registration", InfoInstanceName: "swagger", SwaggerTemplate: docTemplate, LeftDelim: "{{", diff --git a/node-registrar/docs/swagger.json b/node-registrar/docs/swagger.json index d05ca7f..bf638fb 100644 --- a/node-registrar/docs/swagger.json +++ b/node-registrar/docs/swagger.json @@ -1,8 +1,12 @@ { "swagger": "2.0", "info": { - "contact": {} + "description": "API for managing TFGrid node registration", + "title": "Node Registrar API", + "contact": {}, + "version": "1.0" }, + "basePath": "/v1", "paths": { "/accounts": { "get": { @@ -459,6 +463,18 @@ "name": "healthy", "in": "query" }, + { + "type": "boolean", + "description": "Filter by online status (true = online, false = offline)", + "name": "online", + "in": "query" + }, + { + "type": "integer", + "description": "Filter nodes last seen within this many minutes", + "name": "last_seen", + "in": "query" + }, { "type": "integer", "default": 1, @@ -581,7 +597,7 @@ ], "responses": { "200": { - "description": "Node details", + "description": "Node details with last_seen information", "schema": { "$ref": "#/definitions/db.Node" } @@ -883,6 +899,11 @@ }, "db.Farm": { "type": "object", + "required": [ + "farm_name", + "stellar_address", + "twin_id" + ], "properties": { "created_at": { "type": "string" @@ -919,7 +940,10 @@ "type": "object", "properties": { "ips": { - "type": "string" + "type": "array", + "items": { + "type": "string" + } }, "mac": { "type": "string" @@ -965,6 +989,10 @@ "$ref": "#/definitions/db.Interface" } }, + "last_seen": { + "description": "Last time the node sent an uptime report", + "type": "string" + }, "location": { "$ref": "#/definitions/db.Location" }, @@ -1128,8 +1156,7 @@ "maxLength": 40 }, "stellar_address": { - "type": "string", - "maxLength": 56 + "type": "string" } } }, diff --git a/node-registrar/docs/swagger.yaml b/node-registrar/docs/swagger.yaml index d2b3482..af3283f 100644 --- a/node-registrar/docs/swagger.yaml +++ b/node-registrar/docs/swagger.yaml @@ -1,3 +1,4 @@ +basePath: /v1 definitions: db.Account: properties: @@ -50,11 +51,17 @@ definitions: type: integer updated_at: type: string + required: + - farm_name + - stellar_address + - twin_id type: object db.Interface: properties: ips: - type: string + items: + type: string + type: array mac: type: string name: @@ -85,6 +92,9 @@ definitions: items: $ref: '#/definitions/db.Interface' type: array + last_seen: + description: Last time the node sent an uptime report + type: string location: $ref: '#/definitions/db.Location' node_id: @@ -198,7 +208,6 @@ definitions: maxLength: 40 type: string stellar_address: - maxLength: 56 type: string type: object server.UpdateNodeRequest: @@ -238,6 +247,9 @@ definitions: type: object info: contact: {} + description: API for managing TFGrid node registration + title: Node Registrar API + version: "1.0" paths: /accounts: get: @@ -544,6 +556,14 @@ paths: in: query name: healthy type: boolean + - description: Filter by online status (true = online, false = offline) + in: query + name: online + type: boolean + - description: Filter nodes last seen within this many minutes + in: query + name: last_seen + type: integer - default: 1 description: Page number in: query @@ -629,7 +649,7 @@ paths: - application/json responses: "200": - description: Node details + description: Node details with last_seen information schema: $ref: '#/definitions/db.Node' "400": diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index 7427dfe..af0ea7b 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -48,6 +48,7 @@ type Node struct { SerialNumber string `json:"serial_number"` UptimeReports []UptimeReport `json:"uptime" gorm:"foreignKey:NodeID;references:NodeID;constraint:OnDelete:CASCADE"` + LastSeen *time.Time `json:"last_seen" gorm:"index"` // Last time the node sent an uptime report CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` @@ -89,11 +90,13 @@ type Location struct { } type NodeFilter struct { - NodeID *uint64 `form:"node_id"` - FarmID *uint64 `form:"farm_id"` - TwinID *uint64 `form:"twin_id"` - Status string `form:"status"` - Healthy bool `form:"healthy"` + NodeID *uint64 `form:"node_id"` + FarmID *uint64 `form:"farm_id"` + TwinID *uint64 `form:"twin_id"` + Status string `form:"status"` + Healthy bool `form:"healthy"` + Online *bool `form:"online"` // Filter by online status (true = online, false = offline, nil = both) + LastSeen *int64 `form:"last_seen"` // Filter nodes last seen within this many minutes } type FarmFilter struct { diff --git a/node-registrar/pkg/db/nodes.go b/node-registrar/pkg/db/nodes.go index aa52f5d..8077312 100644 --- a/node-registrar/pkg/db/nodes.go +++ b/node-registrar/pkg/db/nodes.go @@ -23,6 +23,28 @@ func (db *Database) ListNodes(filter NodeFilter, limit Limit) (nodes []Node, err query = query.Where("twin_id = ?", *filter.TwinID) } + // Filter by online status (node sent an uptime report in the last 30 minutes) + if filter.Online != nil { + // Calculate the cutoff time (30 minutes ago by default) + cutoffMinutes := int64(30) // Default to 30 minutes + if filter.LastSeen != nil { + cutoffMinutes = *filter.LastSeen + } + cutoffTime := time.Now().Add(-time.Duration(cutoffMinutes) * time.Minute) + + if *filter.Online { + // Online nodes: last_seen is not null and more recent than cutoff time + query = query.Where("last_seen IS NOT NULL AND last_seen > ?", cutoffTime) + } else { + // Offline nodes: last_seen is null or older than cutoff time + query = query.Where("last_seen IS NULL OR last_seen <= ?", cutoffTime) + } + } else if filter.LastSeen != nil { + // If only LastSeen is provided without Online flag, show nodes active within that period + cutoffTime := time.Now().Add(-time.Duration(*filter.LastSeen) * time.Minute) + query = query.Where("last_seen IS NOT NULL AND last_seen > ?", cutoffTime) + } + offset := (limit.Page - 1) * limit.Size query = query.Offset(int(offset)).Limit(int(limit.Size)) @@ -78,7 +100,27 @@ func (db *Database) GetUptimeReports(nodeID uint64, start, end time.Time) ([]Upt } func (db *Database) CreateUptimeReport(report *UptimeReport) error { - return db.gormDB.Create(report).Error + // Start a transaction + tx := db.gormDB.Begin() + if tx.Error != nil { + return tx.Error + } + + // Create the uptime report + if err := tx.Create(report).Error; err != nil { + tx.Rollback() + return err + } + + // Update the node's LastSeen field + now := time.Now() + if err := tx.Model(&Node{}).Where("node_id = ?", report.NodeID).Update("last_seen", now).Error; err != nil { + tx.Rollback() + return err + } + + // Commit the transaction + return tx.Commit().Error } func (db *Database) SetZOSVersion(version string) error { diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 0e2534e..1f13789 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -211,6 +211,8 @@ func (s Server) updateFarmHandler(c *gin.Context) { // @Param twin_id query int false "Filter by twin ID" // @Param status query string false "Filter by status" // @Param healthy query bool false "Filter by health status" +// @Param online query bool false "Filter by online status (true = online, false = offline)" +// @Param last_seen query int false "Filter nodes last seen within this many minutes" // @Param page query int false "Page number" default(1) // @Param size query int false "Results per page" default(10) // @Success 200 {object} []db.Node "List of nodes" @@ -241,7 +243,7 @@ func (s Server) listNodesHandler(c *gin.Context) { // @Accept json // @Produce json // @Param node_id path int true "Node ID" -// @Success 200 {object} db.Node "Node details" +// @Success 200 {object} db.Node "Node details with last_seen information" // @Failure 400 {object} map[string]any "Invalid node ID" // @Failure 404 {object} map[string]any "Node not found" // @Router /nodes/{node_id} [get] From d1558b48f280b3e740411a77a611085ea41be602 Mon Sep 17 00:00:00 2001 From: xmonader Date: Wed, 23 Apr 2025 17:19:42 +0200 Subject: [PATCH 2/3] Add online to node and nodes handlers --- node-registrar/docs/docs.go | 10 +++--- node-registrar/docs/swagger.json | 10 +++--- node-registrar/docs/swagger.yaml | 10 +++--- node-registrar/pkg/server/handlers.go | 49 ++++++++++++++++++++++++--- 4 files changed, 63 insertions(+), 16 deletions(-) diff --git a/node-registrar/docs/docs.go b/node-registrar/docs/docs.go index 83a8873..a2897b3 100644 --- a/node-registrar/docs/docs.go +++ b/node-registrar/docs/docs.go @@ -499,11 +499,12 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "List of nodes", + "description": "List of nodes with online status", "schema": { "type": "array", "items": { - "$ref": "#/definitions/db.Node" + "type": "object", + "additionalProperties": true } } }, @@ -604,9 +605,10 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "Node details with last_seen information", + "description": "Node details with online status and last_seen information", "schema": { - "$ref": "#/definitions/db.Node" + "type": "object", + "additionalProperties": true } }, "400": { diff --git a/node-registrar/docs/swagger.json b/node-registrar/docs/swagger.json index bf638fb..f61fa25 100644 --- a/node-registrar/docs/swagger.json +++ b/node-registrar/docs/swagger.json @@ -492,11 +492,12 @@ ], "responses": { "200": { - "description": "List of nodes", + "description": "List of nodes with online status", "schema": { "type": "array", "items": { - "$ref": "#/definitions/db.Node" + "type": "object", + "additionalProperties": true } } }, @@ -597,9 +598,10 @@ ], "responses": { "200": { - "description": "Node details with last_seen information", + "description": "Node details with online status and last_seen information", "schema": { - "$ref": "#/definitions/db.Node" + "type": "object", + "additionalProperties": true } }, "400": { diff --git a/node-registrar/docs/swagger.yaml b/node-registrar/docs/swagger.yaml index af3283f..a9d567c 100644 --- a/node-registrar/docs/swagger.yaml +++ b/node-registrar/docs/swagger.yaml @@ -578,10 +578,11 @@ paths: - application/json responses: "200": - description: List of nodes + description: List of nodes with online status schema: items: - $ref: '#/definitions/db.Node' + additionalProperties: true + type: object type: array "400": description: Bad request @@ -649,9 +650,10 @@ paths: - application/json responses: "200": - description: Node details with last_seen information + description: Node details with online status and last_seen information schema: - $ref: '#/definitions/db.Node' + additionalProperties: true + type: object "400": description: Invalid node ID schema: diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 1f13789..ec95d03 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -215,7 +215,7 @@ func (s Server) updateFarmHandler(c *gin.Context) { // @Param last_seen query int false "Filter nodes last seen within this many minutes" // @Param page query int false "Page number" default(1) // @Param size query int false "Results per page" default(10) -// @Success 200 {object} []db.Node "List of nodes" +// @Success 200 {object} []map[string]interface{} "List of nodes with online status" // @Failure 400 {object} map[string]any "Bad request" // @Router /nodes [get] func (s Server) listNodesHandler(c *gin.Context) { @@ -234,7 +234,28 @@ func (s Server) listNodesHandler(c *gin.Context) { return } - c.JSON(http.StatusOK, nodes) + // Enhance nodes with online status information + response := make([]gin.H, len(nodes)) + cutoffTime := time.Now().Add(-30 * time.Minute) + + for i, node := range nodes { + // Determine if the node is online + isOnline := false + var lastSeenFormatted string + + if node.LastSeen != nil { + isOnline = node.LastSeen.After(cutoffTime) + lastSeenFormatted = node.LastSeen.Format(time.RFC3339) + } + + response[i] = gin.H{ + "node": node, + "online": isOnline, + "last_seen": lastSeenFormatted, + } + } + + c.JSON(http.StatusOK, response) } // @Summary Get node details @@ -243,7 +264,7 @@ func (s Server) listNodesHandler(c *gin.Context) { // @Accept json // @Produce json // @Param node_id path int true "Node ID" -// @Success 200 {object} db.Node "Node details with last_seen information" +// @Success 200 {object} map[string]interface{} "Node details with online status and last_seen information" // @Failure 400 {object} map[string]any "Invalid node ID" // @Failure 404 {object} map[string]any "Node not found" // @Router /nodes/{node_id} [get] @@ -267,7 +288,27 @@ func (s Server) getNodeHandler(c *gin.Context) { return } - c.JSON(http.StatusOK, node) + // Determine if the node is online (has sent an uptime report in the last 30 minutes) + isOnline := false + var lastSeenFormatted string + + if node.LastSeen != nil { + // Calculate if the node is online (last seen within the last 30 minutes) + cutoffTime := time.Now().Add(-30 * time.Minute) + isOnline = node.LastSeen.After(cutoffTime) + + // Format the last seen time + lastSeenFormatted = node.LastSeen.Format(time.RFC3339) + } + + // Create a response with the node data plus online status + response := gin.H{ + "node": node, + "online": isOnline, + "last_seen": lastSeenFormatted, + } + + c.JSON(http.StatusOK, response) } type NodeRegistrationRequest struct { From 7caa355154022f0ad91bce7983b6c61e7437cc44 Mon Sep 17 00:00:00 2001 From: xmonader Date: Wed, 23 Apr 2025 17:32:01 +0200 Subject: [PATCH 3/3] respect the data shape of the response --- node-registrar/client/types.go | 1 + node-registrar/docs/docs.go | 10 +++--- node-registrar/docs/swagger.json | 10 +++--- node-registrar/docs/swagger.yaml | 9 +++--- node-registrar/pkg/db/models.go | 1 + node-registrar/pkg/server/handlers.go | 44 ++++++--------------------- 6 files changed, 28 insertions(+), 47 deletions(-) diff --git a/node-registrar/client/types.go b/node-registrar/client/types.go index 1680a6b..bbc0bfd 100644 --- a/node-registrar/client/types.go +++ b/node-registrar/client/types.go @@ -31,6 +31,7 @@ type Node struct { SerialNumber string `json:"serial_number"` UptimeReports []UptimeReport `json:"uptime"` LastSeen *time.Time `json:"last_seen"` + Online bool `json:"online"` Approved bool } diff --git a/node-registrar/docs/docs.go b/node-registrar/docs/docs.go index a2897b3..40122cc 100644 --- a/node-registrar/docs/docs.go +++ b/node-registrar/docs/docs.go @@ -503,8 +503,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "type": "object", - "additionalProperties": true + "$ref": "#/definitions/db.Node" } } }, @@ -607,8 +606,7 @@ const docTemplate = `{ "200": { "description": "Node details with online status and last_seen information", "schema": { - "type": "object", - "additionalProperties": true + "$ref": "#/definitions/db.Node" } }, "400": { @@ -1008,6 +1006,10 @@ const docTemplate = `{ "node_id": { "type": "integer" }, + "online": { + "description": "Computed field, not stored in database", + "type": "boolean" + }, "resources": { "description": "PublicConfig PublicConfig ` + "`" + `json:\"public_config\" gorm:\"type:json\"` + "`" + `", "allOf": [ diff --git a/node-registrar/docs/swagger.json b/node-registrar/docs/swagger.json index f61fa25..b0e3f4e 100644 --- a/node-registrar/docs/swagger.json +++ b/node-registrar/docs/swagger.json @@ -496,8 +496,7 @@ "schema": { "type": "array", "items": { - "type": "object", - "additionalProperties": true + "$ref": "#/definitions/db.Node" } } }, @@ -600,8 +599,7 @@ "200": { "description": "Node details with online status and last_seen information", "schema": { - "type": "object", - "additionalProperties": true + "$ref": "#/definitions/db.Node" } }, "400": { @@ -1001,6 +999,10 @@ "node_id": { "type": "integer" }, + "online": { + "description": "Computed field, not stored in database", + "type": "boolean" + }, "resources": { "description": "PublicConfig PublicConfig `json:\"public_config\" gorm:\"type:json\"`", "allOf": [ diff --git a/node-registrar/docs/swagger.yaml b/node-registrar/docs/swagger.yaml index a9d567c..c431314 100644 --- a/node-registrar/docs/swagger.yaml +++ b/node-registrar/docs/swagger.yaml @@ -99,6 +99,9 @@ definitions: $ref: '#/definitions/db.Location' node_id: type: integer + online: + description: Computed field, not stored in database + type: boolean resources: allOf: - $ref: '#/definitions/db.Resources' @@ -581,8 +584,7 @@ paths: description: List of nodes with online status schema: items: - additionalProperties: true - type: object + $ref: '#/definitions/db.Node' type: array "400": description: Bad request @@ -652,8 +654,7 @@ paths: "200": description: Node details with online status and last_seen information schema: - additionalProperties: true - type: object + $ref: '#/definitions/db.Node' "400": description: Invalid node ID schema: diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index af0ea7b..3524ab1 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -49,6 +49,7 @@ type Node struct { UptimeReports []UptimeReport `json:"uptime" gorm:"foreignKey:NodeID;references:NodeID;constraint:OnDelete:CASCADE"` LastSeen *time.Time `json:"last_seen" gorm:"index"` // Last time the node sent an uptime report + Online bool `json:"online" gorm:"-"` // Computed field, not stored in database CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index ec95d03..b134ac2 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -215,7 +215,7 @@ func (s Server) updateFarmHandler(c *gin.Context) { // @Param last_seen query int false "Filter nodes last seen within this many minutes" // @Param page query int false "Page number" default(1) // @Param size query int false "Results per page" default(10) -// @Success 200 {object} []map[string]interface{} "List of nodes with online status" +// @Success 200 {object} []db.Node "List of nodes with online status" // @Failure 400 {object} map[string]any "Bad request" // @Router /nodes [get] func (s Server) listNodesHandler(c *gin.Context) { @@ -234,28 +234,15 @@ func (s Server) listNodesHandler(c *gin.Context) { return } - // Enhance nodes with online status information - response := make([]gin.H, len(nodes)) + // Set online status for each node cutoffTime := time.Now().Add(-30 * time.Minute) - - for i, node := range nodes { - // Determine if the node is online - isOnline := false - var lastSeenFormatted string - - if node.LastSeen != nil { - isOnline = node.LastSeen.After(cutoffTime) - lastSeenFormatted = node.LastSeen.Format(time.RFC3339) - } - - response[i] = gin.H{ - "node": node, - "online": isOnline, - "last_seen": lastSeenFormatted, + for i := range nodes { + if nodes[i].LastSeen != nil { + nodes[i].Online = nodes[i].LastSeen.After(cutoffTime) } } - c.JSON(http.StatusOK, response) + c.JSON(http.StatusOK, nodes) } // @Summary Get node details @@ -264,7 +251,7 @@ func (s Server) listNodesHandler(c *gin.Context) { // @Accept json // @Produce json // @Param node_id path int true "Node ID" -// @Success 200 {object} map[string]interface{} "Node details with online status and last_seen information" +// @Success 200 {object} db.Node "Node details with online status and last_seen information" // @Failure 400 {object} map[string]any "Invalid node ID" // @Failure 404 {object} map[string]any "Node not found" // @Router /nodes/{node_id} [get] @@ -289,26 +276,13 @@ func (s Server) getNodeHandler(c *gin.Context) { } // Determine if the node is online (has sent an uptime report in the last 30 minutes) - isOnline := false - var lastSeenFormatted string - if node.LastSeen != nil { // Calculate if the node is online (last seen within the last 30 minutes) cutoffTime := time.Now().Add(-30 * time.Minute) - isOnline = node.LastSeen.After(cutoffTime) - - // Format the last seen time - lastSeenFormatted = node.LastSeen.Format(time.RFC3339) - } - - // Create a response with the node data plus online status - response := gin.H{ - "node": node, - "online": isOnline, - "last_seen": lastSeenFormatted, + node.Online = node.LastSeen.After(cutoffTime) } - c.JSON(http.StatusOK, response) + c.JSON(http.StatusOK, node) } type NodeRegistrationRequest struct {