From df7195b58d4ac735c7f980d89400a5dc5655df49 Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Thu, 23 Jan 2025 14:20:40 +0000 Subject: [PATCH 01/31] update registrar node --- node-registrar/cmds/docs/docs.go | 36 ++++++++++++++ node-registrar/cmds/docs/swagger.json | 7 +++ node-registrar/cmds/docs/swagger.yaml | 4 ++ node-registrar/pkg/db/models.go | 68 +++++++++++++++++++++------ node-registrar/pkg/db/nodes.go | 7 +++ 5 files changed, 107 insertions(+), 15 deletions(-) create mode 100644 node-registrar/cmds/docs/docs.go create mode 100644 node-registrar/cmds/docs/swagger.json create mode 100644 node-registrar/cmds/docs/swagger.yaml diff --git a/node-registrar/cmds/docs/docs.go b/node-registrar/cmds/docs/docs.go new file mode 100644 index 0000000..6676994 --- /dev/null +++ b/node-registrar/cmds/docs/docs.go @@ -0,0 +1,36 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": {} +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "", + Host: "", + BasePath: "", + Schemes: []string{}, + Title: "", + Description: "", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/node-registrar/cmds/docs/swagger.json b/node-registrar/cmds/docs/swagger.json new file mode 100644 index 0000000..ec416cd --- /dev/null +++ b/node-registrar/cmds/docs/swagger.json @@ -0,0 +1,7 @@ +{ + "swagger": "2.0", + "info": { + "contact": {} + }, + "paths": {} +} \ No newline at end of file diff --git a/node-registrar/cmds/docs/swagger.yaml b/node-registrar/cmds/docs/swagger.yaml new file mode 100644 index 0000000..b64379c --- /dev/null +++ b/node-registrar/cmds/docs/swagger.yaml @@ -0,0 +1,4 @@ +info: + contact: {} +paths: {} +swagger: "2.0" diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index d2c2f2b..37c1532 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -9,6 +9,11 @@ import ( substrate "github.com/threefoldtech/tfchain/clients/tfchain-client-go" ) +type Twin struct { + TwinID uint64 + // pubkey +} + type Farm struct { FarmID uint64 `gorm:"primaryKey;autoIncrement" json:"farm_id"` FarmName string `gorm:"size:40;not null;unique;check:farm_name <> ''" json:"farm_name"` @@ -32,8 +37,12 @@ type Node struct { Location Location `json:"location" gorm:"not null;type:json"` - PublicConfig PublicConfig `json:"public_config" gorm:"type:json"` - Resources Resources `json:"resources" gorm:"not null;type:json"` + // PublicConfig PublicConfig `json:"public_config" gorm:"type:json"` + Resources Resources `json:"resources" gorm:"not null;type:json"` + Interface Interface `json:"interface" gorm:"not null;type:json"` + SecureBoot bool + Virtualized bool + SerialNumber string Uptime Uptime `json:"uptime"` Consumption Consumption `json:"consumption" gorm:"type:jsonb;serializer:json"` @@ -49,30 +58,30 @@ type Consumption []substrate.NruConsumption type Uptime int64 -type PublicConfig struct { - PublicIPV4 string `json:"public_ip_v4"` - PublicIPV6 string `json:"public_ip_v6"` - Domain string `json:"domain"` +type Interface struct { + Name string `json:"name"` + Mac string `json:"mac"` + IPs string `json:"ips"` } -// Value implements the Valuer interface for storing PublicConfig in the database -func (c PublicConfig) Value() (driver.Value, error) { - bytes, err := json.Marshal(c) +// Value implements the Valuer interface for storing Interface in the database +func (i Interface) Value() (driver.Value, error) { + bytes, err := json.Marshal(i) if err != nil { - return nil, fmt.Errorf("failed to marshal PublicConfig: %w", err) + return nil, fmt.Errorf("failed to marshal Interface: %w", err) } return string(bytes), nil } -// Scan implements the Scanner interface for retrieving PublicConfig from the database -func (c *PublicConfig) Scan(value any) error { +// Scan implements the Scanner interface for retrieving Interface from the database +func (i *Interface) Scan(value any) error { bytes, ok := value.([]byte) if !ok { - return fmt.Errorf("invalid data type for PublicConfig: %T", value) + return fmt.Errorf("invalid data type for Interface: %T", value) } - if err := json.Unmarshal(bytes, c); err != nil { - return fmt.Errorf("failed to unmarshal PublicConfig: %w", err) + if err := json.Unmarshal(bytes, i); err != nil { + return fmt.Errorf("failed to unmarshal Interface: %w", err) } return nil } @@ -135,6 +144,35 @@ func (l *Location) Scan(value any) error { return nil } +// type PublicConfig struct { +// PublicIPV4 string `json:"public_ip_v4"` +// PublicIPV6 string `json:"public_ip_v6"` +// Domain string `json:"domain"` +// } +// +// // Value implements the Valuer interface for storing PublicConfig in the database +// +// func (c PublicConfig) Value() (driver.Value, error) { +// bytes, err := json.Marshal(c) +// if err != nil { +// return nil, fmt.Errorf("failed to marshal PublicConfig: %w", err) +// } +// return string(bytes), nil +// } +// +// // Scan implements the Scanner interface for retrieving PublicConfig from the database +// +// func (c *PublicConfig) Scan(value any) error { +// bytes, ok := value.([]byte) +// if !ok { +// return fmt.Errorf("invalid data type for PublicConfig: %T", value) +// } +// +// if err := json.Unmarshal(bytes, c); err != nil { +// return fmt.Errorf("failed to unmarshal PublicConfig: %w", err) +// } +// return nil +// } type NodeFilter struct { NodeID *uint64 `form:"node_id"` FarmID *uint64 `form:"farm_id"` diff --git a/node-registrar/pkg/db/nodes.go b/node-registrar/pkg/db/nodes.go index c187afc..b6f311c 100644 --- a/node-registrar/pkg/db/nodes.go +++ b/node-registrar/pkg/db/nodes.go @@ -46,6 +46,13 @@ func (db *Database) GetNode(nodeID uint64) (node Node, err error) { // RegisterNode registers a new node in the database func (db *Database) RegisterNode(node Node) (err error) { + if result := db.gormDB.First(&node, nodeID); result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return ErrRecordNotFound + } + return result.Error + } + if result := db.gormDB.Create(&node); result.Error != nil { if errors.Is(result.Error, gorm.ErrDuplicatedKey) { return ErrRecordAlreadyExists From 9b6e4dfc93f353475ed1c689c2fe1a96312a302d Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Sun, 26 Jan 2025 19:23:58 +0200 Subject: [PATCH 02/31] add model and handlers for creating and getting accounts --- node-registrar/pkg/db/accounts.go | 28 +++++ node-registrar/pkg/db/models.go | 12 +- node-registrar/pkg/db/nodes.go | 2 +- node-registrar/pkg/server/handlers.go | 152 ++++++++++++++++++++++++++ node-registrar/pkg/server/routes.go | 10 +- 5 files changed, 197 insertions(+), 7 deletions(-) create mode 100644 node-registrar/pkg/db/accounts.go diff --git a/node-registrar/pkg/db/accounts.go b/node-registrar/pkg/db/accounts.go new file mode 100644 index 0000000..9c1f6c3 --- /dev/null +++ b/node-registrar/pkg/db/accounts.go @@ -0,0 +1,28 @@ +package db + +import ( + "errors" + + "gorm.io/gorm" +) + +// CreateAccount creates a new account in the database +func (db *Database) CreateAccount(account *Account) error { + err := db.gormDB.Create(account).Error + if errors.Is(err, gorm.ErrDuplicatedKey) { + return ErrRecordAlreadyExists + } + return err +} + +// GetAccountByTwinID retrieves an account by its twin ID +func (db *Database) GetAccount(twinID uint64) (*Account, error) { + var account Account + if err := db.gormDB.First(&account, twinID).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, ErrRecordNotFound + } + return nil, err + } + return &account, nil +} diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index 37c1532..4d90b05 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -9,9 +9,14 @@ import ( substrate "github.com/threefoldtech/tfchain/clients/tfchain-client-go" ) -type Twin struct { - TwinID uint64 - // pubkey +type Account struct { + TwinID uint64 `gorm:"primaryKey;autoIncrement"` + CreatedAt time.Time + UpdatedAt time.Time + PublicKey string `gorm:"type:text;not null;unique"` // ED25519 public key in the more standard base64 since we are moving from substarte echo system? (still SS58 can be used or plain base58 ,TBD) + // Relations + Farms []Farm `gorm:"foreignKey:TwinID;references:TwinID"` + Nodes []Node `gorm:"foreignKey:TwinID;references:TwinID"` } type Farm struct { @@ -22,7 +27,6 @@ type Farm struct { FarmFreeIps uint64 `json:"farm_free_ips"` CreatedAt time.Time - UpdatedAt time.Time Nodes []Node `gorm:"foreignKey:farm_id;constraint:OnDelete:CASCADE" json:"nodes"` } diff --git a/node-registrar/pkg/db/nodes.go b/node-registrar/pkg/db/nodes.go index b6f311c..dd8628c 100644 --- a/node-registrar/pkg/db/nodes.go +++ b/node-registrar/pkg/db/nodes.go @@ -46,7 +46,7 @@ func (db *Database) GetNode(nodeID uint64) (node Node, err error) { // RegisterNode registers a new node in the database func (db *Database) RegisterNode(node Node) (err error) { - if result := db.gormDB.First(&node, nodeID); result.Error != nil { + if result := db.gormDB.First(&node, node.NodeID); result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return ErrRecordNotFound } diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 4a215af..95ede3f 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -1,10 +1,15 @@ package server import ( + "crypto/ed25519" + "crypto/sha256" + "encoding/base64" + "encoding/hex" "errors" "fmt" "net/http" "strconv" + "time" "github.com/gin-gonic/gin" "github.com/threefoldtech/tfgrid-sdk-go/node-registrar/pkg/db" @@ -378,3 +383,150 @@ func parseQueryParams(c *gin.Context, types_ ...interface{}) error { } return nil } + +// AccountRequest represents the request body for account operations +type AccountCreationRequest struct { + PublicKey string `json:"public_key" binding:"required"` + Signature string `json:"signature" binding:"required"` + Timestamp int64 `json:"timestamp" binding:"required"` +} + +const ( + MaxTimestampDelta = 2 * time.Second +) + +// Challenge is uniquely tied to both the timestamp and public key +// Prevents replay attacks across different accounts, still no state management required +// createChallenge creates a deterministic challenge from timestamp and public key +func createChallenge(timestamp int64, publicKey string) string { + // Create a unique message combining action, timestamp, and public key + message := fmt.Sprintf("create_account:%d:%s", timestamp, publicKey) + + // Hash the message + hash := sha256.Sum256([]byte(message)) + return hex.EncodeToString(hash[:]) +} + +// @Summary create an account/twin +// @Description create an account/twin +// @Accept json +// @Produce json +// @Param public_key body string true "base64 encoded public key" +// @Param signature body string true "base64 encoded signature" +// @Param timestamp body uint64 true "timestamp" +// @Success 201 {object} db.Account +// @Failure 400 {object} error +// @Failure 409 {object} db.ErrRecordAlreadyExists +// @Router /accounts/ [post] +// createAccountHandler creates a new account after verifying key ownership +func (s *Server) createAccountHandler(c *gin.Context) { + var req AccountCreationRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // Validate public key format + if !isValidPublicKey(req.PublicKey) { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid public key format"}) + return + } + + // Verify timestamp is within acceptable window + now := time.Now() + requestTime := time.Unix(req.Timestamp, 0) + delta := now.Sub(requestTime) + + if delta < -MaxTimestampDelta || delta > MaxTimestampDelta { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "timestamp outside acceptable window", + "server_time": now.Unix(), + }) + return + } + + // Create challenge using timestamp and public key + challenge := createChallenge(req.Timestamp, req.PublicKey) + + // Verify signature of the challenge + valid, err := verifySignature(req.PublicKey, challenge, req.Signature) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("signature verification error: %v", err)}) + return + } + + if !valid { + c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid signature"}) + return + } + + // Now we can create new account + account := &db.Account{ + PublicKey: req.PublicKey, + } + + if err := s.db.CreateAccount(account); err != nil { + if errors.Is(err, db.ErrRecordAlreadyExists) { + c.JSON(http.StatusConflict, gin.H{"error": "account with this public key already exists"}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create account"}) + return + } + + c.JSON(http.StatusCreated, account) +} + +// getAccountHandler retrieves an account by twin ID +func (s *Server) getAccountHandler(c *gin.Context) { + twinID, err := strconv.ParseUint(c.Param("twin_id"), 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid twin ID"}) + return + } + + account, err := s.db.GetAccount(twinID) + if err != nil { + if err == db.ErrRecordNotFound { + c.JSON(http.StatusNotFound, gin.H{"error": "account not found"}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get account"}) + return + } + + c.JSON(http.StatusOK, account) +} + +// verifySignature verifies an ED25519 signature +func verifySignature(publicKeyBase64, message, signatureBase64 string) (bool, error) { + // Decode public key from base64 + publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64) + if err != nil { + return false, fmt.Errorf("invalid public key format: %w", err) + } + + // Verify public key length + if len(publicKeyBytes) != ed25519.PublicKeySize { + return false, fmt.Errorf("invalid public key size: expected %d, got %d", + ed25519.PublicKeySize, len(publicKeyBytes)) + } + + // Decode signature from base64 + signatureBytes, err := base64.StdEncoding.DecodeString(signatureBase64) + if err != nil { + return false, fmt.Errorf("invalid signature format: %w", err) + } + + // Verify the signature + return ed25519.Verify(publicKeyBytes, []byte(message), signatureBytes), nil +} + +// Helper function to validate public key format +func isValidPublicKey(publicKeyBase64 string) bool { + publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64) + if err != nil { + return false + } + return len(publicKeyBytes) == ed25519.PublicKeySize +} diff --git a/node-registrar/pkg/server/routes.go b/node-registrar/pkg/server/routes.go index 1b920e7..56ef3ed 100644 --- a/node-registrar/pkg/server/routes.go +++ b/node-registrar/pkg/server/routes.go @@ -3,8 +3,8 @@ package server import ( _ "github.com/threefoldtech/tfgrid-sdk-go/node-registrar/docs" - "github.com/swaggo/files" - "github.com/swaggo/gin-swagger" + swaggerFiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" ) func (s *Server) SetupRoutes() { @@ -29,4 +29,10 @@ func (s *Server) SetupRoutes() { nodeRoutes.Use(s.createAuthNodeMiddleware(s.network)) nodeRoutes.POST("/:node_id/uptime", s.uptimeHandler) nodeRoutes.POST("/:node_id/consumption", s.storeConsumptionHandler) + + // Account routes + accountRoutes := v1.Group("accounts") + accountRoutes.POST("/", s.createAccountHandler) + accountRoutes.GET("/:twin_id", s.getAccountHandler) + } From b0040d3f1b4e3841d02bee60695afed2d8009f27 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Sun, 26 Jan 2025 19:28:15 +0200 Subject: [PATCH 03/31] update swagger doc --- node-registrar/pkg/server/handlers.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 95ede3f..1f6cc49 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -407,8 +407,8 @@ func createChallenge(timestamp int64, publicKey string) string { return hex.EncodeToString(hash[:]) } -// @Summary create an account/twin -// @Description create an account/twin +// @Summary creates a new account/twin +// @Description creates a new account after verifying key ownership // @Accept json // @Produce json // @Param public_key body string true "base64 encoded public key" @@ -418,7 +418,6 @@ func createChallenge(timestamp int64, publicKey string) string { // @Failure 400 {object} error // @Failure 409 {object} db.ErrRecordAlreadyExists // @Router /accounts/ [post] -// createAccountHandler creates a new account after verifying key ownership func (s *Server) createAccountHandler(c *gin.Context) { var req AccountCreationRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -478,6 +477,16 @@ func (s *Server) createAccountHandler(c *gin.Context) { } // getAccountHandler retrieves an account by twin ID +// @Summary Retrieve an account by twin ID +// @Description This endpoint retrieves an account by its twin ID. +// @Tags accounts +// @Accept json +// @Produce json +// @Param twin_id path uint64 true "Twin ID of the account" +// @Success 200 {object} db.Account "Account details" +// @Failure 400 {object} gin.H "Invalid twin ID" +// @Failure 404 {object} gin.H "Account not found" +// @Router /accounts/{twin_id} [get] func (s *Server) getAccountHandler(c *gin.Context) { twinID, err := strconv.ParseUint(c.Param("twin_id"), 10, 64) if err != nil { From 1e607bf18b8141444ff01650882e2f3c204bf81f Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Mon, 27 Jan 2025 12:46:16 +0200 Subject: [PATCH 04/31] support track uptime reports historical data --- node-registrar/pkg/db/models.go | 26 +++++++----- node-registrar/pkg/db/nodes.go | 22 +++++----- node-registrar/pkg/server/handlers.go | 58 +++++++++++++++------------ node-registrar/pkg/server/routes.go | 2 +- 4 files changed, 58 insertions(+), 50 deletions(-) diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index 4d90b05..1e56506 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -15,26 +15,26 @@ type Account struct { UpdatedAt time.Time PublicKey string `gorm:"type:text;not null;unique"` // ED25519 public key in the more standard base64 since we are moving from substarte echo system? (still SS58 can be used or plain base58 ,TBD) // Relations - Farms []Farm `gorm:"foreignKey:TwinID;references:TwinID"` - Nodes []Node `gorm:"foreignKey:TwinID;references:TwinID"` + Farms []Farm `gorm:"foreignKey:TwinID;references:TwinID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL"` } type Farm struct { FarmID uint64 `gorm:"primaryKey;autoIncrement" json:"farm_id"` FarmName string `gorm:"size:40;not null;unique;check:farm_name <> ''" json:"farm_name"` - TwinID uint64 `json:"twin_id" gorm:"not null;check:twin_id > 0"` + TwinID uint64 `json:"twin_id" gorm:"not null;check:twin_id > 0"` // Farmer account refrence Dedicated bool `json:"dedicated"` FarmFreeIps uint64 `json:"farm_free_ips"` CreatedAt time.Time + UpdatedAt time.Time - Nodes []Node `gorm:"foreignKey:farm_id;constraint:OnDelete:CASCADE" json:"nodes"` + Nodes []Node `gorm:"foreignKey:FarmID;references:FarmID;constraint:OnDelete:CASCADE" json:"nodes"` } type Node struct { NodeID uint64 `json:"node_id" gorm:"primaryKey;autoIncrement"` - FarmID uint64 `json:"farm_id" gorm:"not null;check:farm_id> 0"` - TwinID uint64 `json:"twin_id" gorm:"not null;check:twin_id > 0"` + FarmID uint64 `json:"farm_id" gorm:"not null;check:farm_id> 0;foreignKey:FarmID;references:FarmID;constraint:OnDelete:CASCADE"` + TwinID uint64 `json:"twin_id" gorm:"not null;check:twin_id > 0;foreignKey:TwinID;references:TwinID;constraint:OnDelete:CASCADE"` // Node account reference ZosVersion string `json:"zos_version" gorm:"not null"` NodeType string `json:"node_type" gorm:"not null"` @@ -48,8 +48,8 @@ type Node struct { Virtualized bool SerialNumber string - Uptime Uptime `json:"uptime"` - Consumption Consumption `json:"consumption" gorm:"type:jsonb;serializer:json"` + UptimeReports []UptimeReport `json:"uptime" gorm:"foreignKey:NodeID;references:NodeID;constraint:OnDelete:CASCADE"` + Consumption Consumption `json:"consumption" gorm:"type:jsonb;serializer:json"` PriceUsd float64 `json:"price_usd"` Status string `json:"status"` @@ -60,8 +60,14 @@ type Node struct { type Consumption []substrate.NruConsumption -type Uptime int64 - +type UptimeReport struct { + ID uint64 `gorm:"primaryKey;autoIncrement"` + NodeID uint64 `gorm:"index"` + Duration time.Duration // Uptime duration for this period + Timestamp time.Time `gorm:"index"` + WasRestart bool // True if this report followed a restart + CreatedAt time.Time +} type Interface struct { Name string `json:"name"` Mac string `json:"mac"` diff --git a/node-registrar/pkg/db/nodes.go b/node-registrar/pkg/db/nodes.go index dd8628c..e13883d 100644 --- a/node-registrar/pkg/db/nodes.go +++ b/node-registrar/pkg/db/nodes.go @@ -2,6 +2,7 @@ package db import ( "errors" + "time" "gorm.io/gorm" ) @@ -63,20 +64,15 @@ func (db *Database) RegisterNode(node Node) (err error) { } // Uptime updates the uptime for a specific node -func (db *Database) Uptime(nodeID uint64, report Uptime) (err error) { - var node Node - if result := db.gormDB.First(&node, nodeID); result.Error != nil { - if errors.Is(result.Error, gorm.ErrRecordNotFound) { - return ErrRecordNotFound - } - return result.Error - } +func (db *Database) GetUptimeReports(nodeID uint64, start, end time.Time) ([]UptimeReport, error) { + var reports []UptimeReport + result := db.gormDB.Where("node_id = ? AND timestamp BETWEEN ? AND ?", + nodeID, start, end).Order("timestamp asc").Find(&reports) + return reports, result.Error +} - node.Uptime = report - if result := db.gormDB.Save(&node); result.Error != nil { - return result.Error - } - return nil +func (db *Database) CreateUptimeReport(report *UptimeReport) error { + return db.gormDB.Create(report).Error } // Consumption updates the consumption report for a specific node diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 1f6cc49..1af05ea 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -287,49 +287,55 @@ func (s Server) registerNodeHandler(c *gin.Context) { }) } +type UptimeReportRequest struct { + NodeID uint64 `json:"node_id" binding:"required"` + Uptime time.Duration `json:"uptime" binding:"required"` + Timestamp time.Time `json:"timestamp" binding:"required"` +} + // @Summary uptime report // @Description save uptime report of a node // @Accept json // @Produce json -// @Param node_id query uint64 true "node id" -// @Param uptime body db.Uptime false "uptime report" -// @Success 200 {object} string -// @Failure 400 {object} error -// @Failure 404 {object} db.ErrRecordNotFound +// @Param node_id path uint64 true "node id" +// @Param request body UptimeReportRequest true "uptime report request" +// @Success 201 {object} map[string]string "message: uptime reported successfully" +// @Failure 400 {object} map[string]string "error: error message" +// @Failure 404 {object} map[string]string "error: node not found" // @Router /nodes/{node_id}/uptime [post] -func (s Server) uptimeHandler(c *gin.Context) { - nodeID := c.Param("node_id") +func (s *Server) uptimeReportHandler(c *gin.Context) { + var req UptimeReportRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } - id, err := strconv.ParseUint(nodeID, 10, 64) + // Get node and last report + _, err := s.db.GetNode(req.NodeID) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid node id"}) + c.JSON(http.StatusNotFound, gin.H{"error": "node not found"}) return } - var report struct { - Uptime db.Uptime `json:"uptime"` - } + // Detect restarts + // Validate report timing (40min ± 5min window) + // Maybe aggregate reports here and store total uptime? + // The total uptime should accumulate unless the node restarts, which is detected when the reported uptime is less than the previous value. - if err := c.ShouldBindJSON(&report); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return + // Create report record + report := &db.UptimeReport{ + NodeID: req.NodeID, + Duration: req.Uptime, + Timestamp: req.Timestamp, } - err = s.db.Uptime(id, report.Uptime) + err = s.db.CreateUptimeReport(report) if err != nil { - status := http.StatusBadRequest - - if errors.Is(err, db.ErrRecordNotFound) { - status = http.StatusNotFound - } - - c.JSON(status, gin.H{"error": err.Error()}) + c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save report"}) return } - c.JSON(http.StatusOK, gin.H{ - "message": "uptime report received successfully", - }) + c.JSON(http.StatusCreated, gin.H{"message": "uptime reported successfully"}) } // @Summary consumption report diff --git a/node-registrar/pkg/server/routes.go b/node-registrar/pkg/server/routes.go index 56ef3ed..88d0e03 100644 --- a/node-registrar/pkg/server/routes.go +++ b/node-registrar/pkg/server/routes.go @@ -27,7 +27,7 @@ func (s *Server) SetupRoutes() { nodeRoutes.POST("/", s.registerNodeHandler) nodeRoutes.Use(s.createAuthNodeMiddleware(s.network)) - nodeRoutes.POST("/:node_id/uptime", s.uptimeHandler) + nodeRoutes.POST("/:node_id/uptime", s.uptimeReportHandler) nodeRoutes.POST("/:node_id/consumption", s.storeConsumptionHandler) // Account routes From b26614eaf9b5679ed100114c07b1814a2e8a0e59 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Mon, 27 Jan 2025 13:55:19 +0200 Subject: [PATCH 05/31] Add support for storing and updating twin's Relays and RMB_PK --- node-registrar/pkg/db/accounts.go | 15 ++++++++ node-registrar/pkg/db/models.go | 4 ++- node-registrar/pkg/server/handlers.go | 51 +++++++++++++++++++++++++-- node-registrar/pkg/server/routes.go | 1 + 4 files changed, 67 insertions(+), 4 deletions(-) diff --git a/node-registrar/pkg/db/accounts.go b/node-registrar/pkg/db/accounts.go index 9c1f6c3..01acdac 100644 --- a/node-registrar/pkg/db/accounts.go +++ b/node-registrar/pkg/db/accounts.go @@ -15,6 +15,21 @@ func (db *Database) CreateAccount(account *Account) error { return err } +// UpdateAccount updates an account's relays and RMB encryption key +func (db *Database) UpdateAccount(twinID uint64, relays []string, rmbEncKey string) error { + result := db.gormDB.Model(&Account{}).Where("twin_id = ?", twinID).Updates(map[string]interface{}{ + "relays": relays, + "rmb_enc_key": rmbEncKey, + }) + if result.Error != nil { + return result.Error + } + if result.RowsAffected == 0 { + return ErrRecordNotFound + } + return nil +} + // GetAccountByTwinID retrieves an account by its twin ID func (db *Database) GetAccount(twinID uint64) (*Account, error) { var account Account diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index 1e56506..0c62c52 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -10,7 +10,9 @@ import ( ) type Account struct { - TwinID uint64 `gorm:"primaryKey;autoIncrement"` + TwinID uint64 `gorm:"primaryKey;autoIncrement"` + Relays []string `gorm:"type:text[];default:'{}'" json:"relays"` // Optional list of relay domains + RMBEncKey string `gorm:"type:text" json:"rmb_enc_key"` // Optional base64 encoded public key for rmb communication CreatedAt time.Time UpdatedAt time.Time PublicKey string `gorm:"type:text;not null;unique"` // ED25519 public key in the more standard base64 since we are moving from substarte echo system? (still SS58 can be used or plain base58 ,TBD) diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 1af05ea..b345d9a 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -392,9 +392,11 @@ func parseQueryParams(c *gin.Context, types_ ...interface{}) error { // AccountRequest represents the request body for account operations type AccountCreationRequest struct { - PublicKey string `json:"public_key" binding:"required"` - Signature string `json:"signature" binding:"required"` - Timestamp int64 `json:"timestamp" binding:"required"` + PublicKey string `json:"public_key" binding:"required"` + Signature string `json:"signature" binding:"required"` + Timestamp int64 `json:"timestamp" binding:"required"` + Relays []string `json:"relays,omitempty"` + RMBEncKey string `json:"rmb_enc_key,omitempty"` } const ( @@ -482,6 +484,49 @@ func (s *Server) createAccountHandler(c *gin.Context) { c.JSON(http.StatusCreated, account) } +type UpdateAccountRequest struct { + Relays []string `json:"relays"` + RMBEncKey string `json:"rmb_enc_key"` +} + +// updateAccountHandler updates an account's relays and RMB encryption key +// @Summary Update account details +// @Description Updates an account's relays and RMB encryption key +// @Tags accounts +// @Accept json +// @Produce json +// @Param twin_id path uint64 true "Twin ID of the account" +// @Param account body UpdateAccountRequest true "Account details to update" +// @Success 200 {object} gin.H "Account updated successfully" +// @Failure 400 {object} gin.H "Invalid request" +// @Failure 404 {object} gin.H "Account not found" +// @Router /accounts/{twin_id} [patch] +func (s *Server) updateAccountHandler(c *gin.Context) { + twinID, err := strconv.ParseUint(c.Param("twin_id"), 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid twin ID"}) + return + } + + var req UpdateAccountRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"}) + return + } + + err = s.db.UpdateAccount(twinID, req.Relays, req.RMBEncKey) + if err != nil { + if errors.Is(err, db.ErrRecordNotFound) { + c.JSON(http.StatusNotFound, gin.H{"error": "account not found"}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update account"}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "account updated successfully"}) +} + // getAccountHandler retrieves an account by twin ID // @Summary Retrieve an account by twin ID // @Description This endpoint retrieves an account by its twin ID. diff --git a/node-registrar/pkg/server/routes.go b/node-registrar/pkg/server/routes.go index 88d0e03..eabd94a 100644 --- a/node-registrar/pkg/server/routes.go +++ b/node-registrar/pkg/server/routes.go @@ -34,5 +34,6 @@ func (s *Server) SetupRoutes() { accountRoutes := v1.Group("accounts") accountRoutes.POST("/", s.createAccountHandler) accountRoutes.GET("/:twin_id", s.getAccountHandler) + accountRoutes.PATCH("/:twin_id", s.updateAccountHandler) } From cf2b402179f92b4198c15bb2335b97af28a3f9c3 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Mon, 27 Jan 2025 15:50:59 +0200 Subject: [PATCH 06/31] Update models' Constraints --- node-registrar/pkg/db/models.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index 0c62c52..4869417 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -15,9 +15,11 @@ type Account struct { RMBEncKey string `gorm:"type:text" json:"rmb_enc_key"` // Optional base64 encoded public key for rmb communication CreatedAt time.Time UpdatedAt time.Time - PublicKey string `gorm:"type:text;not null;unique"` // ED25519 public key in the more standard base64 since we are moving from substarte echo system? (still SS58 can be used or plain base58 ,TBD) - // Relations - Farms []Farm `gorm:"foreignKey:TwinID;references:TwinID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL"` + // The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substarte echo system? + // (still SS58 can be used or plain base58 ,TBD) + PublicKey string `gorm:"type:text;not null;unique"` + // Relations | likely we need to use OnDelete:RESTRICT (Prevent Twin deletion if farms exist) + Farms []Farm `gorm:"foreignKey:TwinID;references:TwinID;constraint:OnDelete:RESTRICT"` } type Farm struct { @@ -26,17 +28,17 @@ type Farm struct { TwinID uint64 `json:"twin_id" gorm:"not null;check:twin_id > 0"` // Farmer account refrence Dedicated bool `json:"dedicated"` FarmFreeIps uint64 `json:"farm_free_ips"` + CreatedAt time.Time + UpdatedAt time.Time - CreatedAt time.Time - UpdatedAt time.Time - - Nodes []Node `gorm:"foreignKey:FarmID;references:FarmID;constraint:OnDelete:CASCADE" json:"nodes"` + Nodes []Node `gorm:"foreignKey:FarmID;references:FarmID;constraint:OnDelete:RESTRICT" json:"nodes"` } type Node struct { NodeID uint64 `json:"node_id" gorm:"primaryKey;autoIncrement"` - FarmID uint64 `json:"farm_id" gorm:"not null;check:farm_id> 0;foreignKey:FarmID;references:FarmID;constraint:OnDelete:CASCADE"` - TwinID uint64 `json:"twin_id" gorm:"not null;check:twin_id > 0;foreignKey:TwinID;references:TwinID;constraint:OnDelete:CASCADE"` // Node account reference + // Constrainets set to prevents unintended account deletion if linked Farms/nodes exist. + FarmID uint64 `json:"farm_id" gorm:"not null;check:farm_id> 0;foreignKey:FarmID;references:FarmID;constraint:OnDelete:RESTRICT"` + TwinID uint64 `json:"twin_id" gorm:"not null;check:twin_id > 0;foreignKey:TwinID;references:TwinID;constraint:OnDelete:RESTRICT"` // Node account reference ZosVersion string `json:"zos_version" gorm:"not null"` NodeType string `json:"node_type" gorm:"not null"` From 3e1df91943658f144ba8e3e91186c020dcddb799 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Tue, 28 Jan 2025 04:05:49 +0200 Subject: [PATCH 07/31] Add AuthMiddleware --- node-registrar/Makefile | 6 +- node-registrar/cmds/registrar.go | 5 +- node-registrar/go.mod | 31 +- node-registrar/go.sum | 66 +--- node-registrar/pkg/db/accounts.go | 3 +- node-registrar/pkg/db/db.go | 2 + node-registrar/pkg/db/farms.go | 25 +- node-registrar/pkg/db/models.go | 28 +- node-registrar/pkg/db/nodes.go | 21 -- node-registrar/pkg/server/handlers.go | 457 ++++++++++++----------- node-registrar/pkg/server/middlewares.go | 124 ++++++ node-registrar/pkg/server/routes.go | 12 +- node-registrar/pkg/server/stacks.go | 148 -------- node-registrar/pkg/server/util.go | 31 ++ 14 files changed, 450 insertions(+), 509 deletions(-) create mode 100644 node-registrar/pkg/server/middlewares.go delete mode 100644 node-registrar/pkg/server/stacks.go create mode 100644 node-registrar/pkg/server/util.go diff --git a/node-registrar/Makefile b/node-registrar/Makefile index e1d4cbc..b3dd36d 100644 --- a/node-registrar/Makefile +++ b/node-registrar/Makefile @@ -2,7 +2,7 @@ run: go run cmds/registrar.go --postgres-host localhost --postgres-port 5432 --postgres-db postgres --postgres-user postgres --postgres-password password --domain localhost --server-port 8080 postgres: - docker run --name postgres -e POSTGRES_USER=postgres POSTGRES_PASSWORD=password POSTGRES_DB=postgres -p 5432:5432 -d postgres + docker run --name postgres -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=password -e POSTGRES_DB=postgres -p 5432:5432 -d postgres stop-postgres: docker stop postgres && docker rm postgres @@ -12,8 +12,8 @@ build: ## Bulil the server server-start: @go run cmds/registrar.go \ - --server-port :8080 \ - --log-level debug \ + --server-port 8080 \ + --debug \ --domain localhost \ --sql-log-level 4 \ --postgres-host localhost \ diff --git a/node-registrar/cmds/registrar.go b/node-registrar/cmds/registrar.go index ebea1ec..0da4eaf 100644 --- a/node-registrar/cmds/registrar.go +++ b/node-registrar/cmds/registrar.go @@ -95,6 +95,7 @@ func Run() error { signal.Notify(quit, os.Interrupt, syscall.SIGTERM) log.Info().Msg("server is running on port :8080") + err = s.Run(quit, fmt.Sprintf("%s:%d", f.domain, f.serverPort)) if err != nil { return errors.Wrap(err, "failed to run gin server") @@ -104,7 +105,7 @@ func Run() error { } func (f flags) validate() error { - if f.serverPort < 1 && f.serverPort > 65535 { + if f.serverPort < 1 || f.serverPort > 65535 { return errors.Errorf("invalid port %d, server port should be in the valid port range 1–65535", f.serverPort) } @@ -112,7 +113,7 @@ func (f flags) validate() error { return errors.New("invalid domain name, domain name should not be empty") } if _, err := net.LookupHost(f.domain); err != nil { - return errors.Wrapf(err, "invalid domain %s", f.PostgresHost) + return errors.Wrapf(err, "invalid domain %s", f.domain) } return f.Config.Validate() diff --git a/node-registrar/go.mod b/node-registrar/go.mod index 9254d7a..a3689b6 100644 --- a/node-registrar/go.mod +++ b/node-registrar/go.mod @@ -1,16 +1,18 @@ module github.com/threefoldtech/tfgrid-sdk-go/node-registrar -go 1.21 +go 1.22.0 + +toolchain go1.22.1 require ( github.com/gin-gonic/gin v1.9.0 + github.com/lib/pq v1.10.9 github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.33.0 github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/swag v1.16.4 - github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-20241127100051-77e684bcb1b2 - github.com/threefoldtech/tfgrid-sdk-go/grid-client v0.16.2 + github.com/vedhavyas/go-subkey/v2 v2.0.0 gorm.io/driver/postgres v1.5.7 gorm.io/gorm v1.25.10 ) @@ -21,13 +23,12 @@ require ( github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/bytedance/sonic v1.8.0 // indirect - github.com/cenkalti/backoff v2.2.1+incompatible // indirect - github.com/centrifuge/go-substrate-rpc-client/v4 v4.0.12 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect - github.com/deckarep/golang-set v1.8.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/base58 v1.0.5 // indirect github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/ethereum/go-ethereum v1.11.6 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect @@ -37,9 +38,7 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.11.2 // indirect - github.com/go-stack/stack v1.8.1 // indirect github.com/goccy/go-json v0.10.0 // indirect - github.com/gorilla/websocket v1.5.3 // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect github.com/holiman/uint256 v1.2.3 // indirect @@ -47,12 +46,12 @@ require ( github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.5.5 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect - github.com/jbenet/go-base58 v0.0.0-20150317085156-6237cf65f3a6 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -61,20 +60,18 @@ require ( github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect - github.com/pierrec/xxHash v0.1.5 // indirect - github.com/rs/cors v1.10.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/stretchr/testify v1.10.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.9 // indirect - github.com/vedhavyas/go-subkey v1.0.3 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect - golang.org/x/crypto v0.31.0 // indirect - golang.org/x/net v0.26.0 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/net v0.34.0 // indirect golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.29.0 // indirect google.golang.org/protobuf v1.34.2 // indirect - gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/node-registrar/go.sum b/node-registrar/go.sum index f84b825..762015e 100644 --- a/node-registrar/go.sum +++ b/node-registrar/go.sum @@ -6,20 +6,11 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= -github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= -github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA= github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= -github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/centrifuge/go-substrate-rpc-client/v4 v4.0.12 h1:DCYWIBOalB0mKKfUg2HhtGgIkBbMA1fnlnkZp7fHB18= -github.com/centrifuge/go-substrate-rpc-client/v4 v4.0.12/go.mod h1:5g1oM4Zu3BOaLpsKQ+O8PAv2kNuq+kPcA1VzFbsSqxE= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= @@ -31,10 +22,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= -github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= -github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= -github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/base58 v1.0.5 h1:hwcieUM3pfPnE/6p3J100zoRfGkQxBulZHo7GZfOqic= github.com/decred/base58 v1.0.5/go.mod h1:s/8lukEHFA6bUQQb/v3rjUySJ2hu+RioCzLukAVkrfw= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= @@ -49,8 +36,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -69,18 +54,12 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64= -github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= @@ -95,8 +74,6 @@ github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/jbenet/go-base58 v0.0.0-20150317085156-6237cf65f3a6 h1:4zOlv2my+vf98jT1nQt4bT/yKWUImevYPJ2H344CloE= -github.com/jbenet/go-base58 v0.0.0-20150317085156-6237cf65f3a6/go.mod h1:r/8JmuR0qjuCiEhAolkfvdZgmPiHTnJaG0UXCSeR1Zo= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= @@ -116,6 +93,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= @@ -136,8 +115,7 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/pierrec/xxHash v0.1.5 h1:n/jBpwTHiER4xYvK3/CdPVnLDPchj8eTJFFLUb4QHBo= -github.com/pierrec/xxHash v0.1.5/go.mod h1:w2waW5Zoa/Wc4Yqe0wgrIYAGKqRMf7czn2HNKXmuL+I= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -145,13 +123,9 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= -github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= -github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= -github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -170,39 +144,31 @@ github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+z github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A= github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg= -github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-20241127100051-77e684bcb1b2 h1:VW2J36F8g/kJn4IkY0JiRFmb1gFcdjiOyltfJLJ0mYU= -github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-20241127100051-77e684bcb1b2/go.mod h1:cOL5YgHUmDG5SAXrsZxFjUECRQQuAqOoqvXhZG5sEUw= -github.com/threefoldtech/tfgrid-sdk-go/grid-client v0.16.2 h1:jJrc4gufT0R3uKJ8yAjIidmNIDAvgpmtOXGCpYwzP94= -github.com/threefoldtech/tfgrid-sdk-go/grid-client v0.16.2/go.mod h1:HoeumebUKXaRmyCZ4xxEW4fOgYm3b9vG3AyabfgxuY0= -github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo= -github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= -github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= -github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU= github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/vedhavyas/go-subkey v1.0.3 h1:iKR33BB/akKmcR2PMlXPBeeODjWLM90EL98OrOGs8CA= -github.com/vedhavyas/go-subkey v1.0.3/go.mod h1:CloUaFQSSTdWnINfBRFjVMkWXZANW+nd8+TI5jYcl6Y= +github.com/vedhavyas/go-subkey/v2 v2.0.0 h1:LemDIsrVtRSOkp0FA8HxP6ynfKjeOj3BY2U9UNfeDMA= +github.com/vedhavyas/go-subkey/v2 v2.0.0/go.mod h1:95aZ+XDCWAUUynjlmi7BtPExjXgXxByE0WfBwbmIRH4= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= @@ -218,8 +184,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -233,8 +199,8 @@ golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= @@ -243,8 +209,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/node-registrar/pkg/db/accounts.go b/node-registrar/pkg/db/accounts.go index 01acdac..2642b2f 100644 --- a/node-registrar/pkg/db/accounts.go +++ b/node-registrar/pkg/db/accounts.go @@ -3,6 +3,7 @@ package db import ( "errors" + "github.com/lib/pq" "gorm.io/gorm" ) @@ -16,7 +17,7 @@ func (db *Database) CreateAccount(account *Account) error { } // UpdateAccount updates an account's relays and RMB encryption key -func (db *Database) UpdateAccount(twinID uint64, relays []string, rmbEncKey string) error { +func (db *Database) UpdateAccount(twinID uint64, relays pq.StringArray, rmbEncKey string) error { result := db.gormDB.Model(&Account{}).Where("twin_id = ?", twinID).Updates(map[string]interface{}{ "relays": relays, "rmb_enc_key": rmbEncKey, diff --git a/node-registrar/pkg/db/db.go b/node-registrar/pkg/db/db.go index c441dc2..3de0a1c 100644 --- a/node-registrar/pkg/db/db.go +++ b/node-registrar/pkg/db/db.go @@ -73,8 +73,10 @@ func openDatabase(c Config) (db Database, err error) { func (db Database) autoMigrate() error { if err := db.gormDB.AutoMigrate( + &Account{}, &Farm{}, &Node{}, + &UptimeReport{}, ); err != nil { return errors.Wrap(err, "failed to migrate tables") } diff --git a/node-registrar/pkg/db/farms.go b/node-registrar/pkg/db/farms.go index 09db256..014f671 100644 --- a/node-registrar/pkg/db/farms.go +++ b/node-registrar/pkg/db/farms.go @@ -49,24 +49,15 @@ func (db *Database) CreateFarm(farm Farm) (err error) { return } -func (db *Database) UpdateFarm(farmID uint64, val Farm) (err error) { - var farm Farm - if result := db.gormDB.First(&farm, farmID); result.Error != nil { - if errors.Is(result.Error, gorm.ErrRecordNotFound) { - return ErrRecordNotFound - } +func (db *Database) UpdateFarm(farmID uint64, name string) (err error) { + result := db.gormDB.Model(&Farm{}).Where("farm_id = ?", farmID).Updates(map[string]interface{}{ + "farm_name": name, + }) + if result.Error != nil { return result.Error } - - if val.FarmName != "" { - farm.FarmName = val.FarmName - } - - if val.FarmFreeIps != 0 { - farm.FarmFreeIps = val.FarmFreeIps + if result.RowsAffected == 0 { + return ErrRecordNotFound } - - farm.Dedicated = val.Dedicated - - return db.gormDB.Save(&farm).Error + return nil } diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index 4869417..6aaa592 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -6,16 +6,16 @@ import ( "fmt" "time" - substrate "github.com/threefoldtech/tfchain/clients/tfchain-client-go" + "github.com/lib/pq" ) type Account struct { - TwinID uint64 `gorm:"primaryKey;autoIncrement"` - Relays []string `gorm:"type:text[];default:'{}'" json:"relays"` // Optional list of relay domains - RMBEncKey string `gorm:"type:text" json:"rmb_enc_key"` // Optional base64 encoded public key for rmb communication + TwinID uint64 `gorm:"primaryKey;autoIncrement"` + Relays pq.StringArray `gorm:"type:text[];default:'{}'" json:"relays"` // Optional list of relay domains + RMBEncKey string `gorm:"type:text" json:"rmb_enc_key"` // Optional base64 encoded public key for rmb communication CreatedAt time.Time UpdatedAt time.Time - // The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substarte echo system? + // The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substrate echo system? // (still SS58 can be used or plain base58 ,TBD) PublicKey string `gorm:"type:text;not null;unique"` // Relations | likely we need to use OnDelete:RESTRICT (Prevent Twin deletion if farms exist) @@ -25,7 +25,7 @@ type Account struct { type Farm struct { FarmID uint64 `gorm:"primaryKey;autoIncrement" json:"farm_id"` FarmName string `gorm:"size:40;not null;unique;check:farm_name <> ''" json:"farm_name"` - TwinID uint64 `json:"twin_id" gorm:"not null;check:twin_id > 0"` // Farmer account refrence + TwinID uint64 `json:"twin_id" gorm:"not null;check:twin_id > 0"` // Farmer account reference Dedicated bool `json:"dedicated"` FarmFreeIps uint64 `json:"farm_free_ips"` CreatedAt time.Time @@ -36,34 +36,26 @@ type Farm struct { type Node struct { NodeID uint64 `json:"node_id" gorm:"primaryKey;autoIncrement"` - // Constrainets set to prevents unintended account deletion if linked Farms/nodes exist. + // Constraints set to prevents unintended account deletion if linked Farms/nodes exist. FarmID uint64 `json:"farm_id" gorm:"not null;check:farm_id> 0;foreignKey:FarmID;references:FarmID;constraint:OnDelete:RESTRICT"` TwinID uint64 `json:"twin_id" gorm:"not null;check:twin_id > 0;foreignKey:TwinID;references:TwinID;constraint:OnDelete:RESTRICT"` // Node account reference - ZosVersion string `json:"zos_version" gorm:"not null"` - NodeType string `json:"node_type" gorm:"not null"` - Location Location `json:"location" gorm:"not null;type:json"` // PublicConfig PublicConfig `json:"public_config" gorm:"type:json"` - Resources Resources `json:"resources" gorm:"not null;type:json"` - Interface Interface `json:"interface" gorm:"not null;type:json"` + Resources Resources `json:"resources" gorm:"not null;type:json"` + Interfaces []Interface `json:"interface" gorm:"not null;type:json"` SecureBoot bool Virtualized bool SerialNumber string UptimeReports []UptimeReport `json:"uptime" gorm:"foreignKey:NodeID;references:NodeID;constraint:OnDelete:CASCADE"` - Consumption Consumption `json:"consumption" gorm:"type:jsonb;serializer:json"` - - PriceUsd float64 `json:"price_usd"` - Status string `json:"status"` CreatedAt time.Time UpdatedAt time.Time + Approved bool } -type Consumption []substrate.NruConsumption - type UptimeReport struct { ID uint64 `gorm:"primaryKey;autoIncrement"` NodeID uint64 `gorm:"index"` diff --git a/node-registrar/pkg/db/nodes.go b/node-registrar/pkg/db/nodes.go index e13883d..c1f244d 100644 --- a/node-registrar/pkg/db/nodes.go +++ b/node-registrar/pkg/db/nodes.go @@ -20,9 +20,6 @@ func (db *Database) ListNodes(filter NodeFilter, limit Limit) (nodes []Node, err if filter.TwinID != nil { query = query.Where("twin_id = ?", *filter.TwinID) } - if filter.Status != "" { - query = query.Where("status = ?", filter.Status) - } offset := (limit.Page - 1) * limit.Size query = query.Offset(int(offset)).Limit(int(limit.Size)) @@ -74,21 +71,3 @@ func (db *Database) GetUptimeReports(nodeID uint64, start, end time.Time) ([]Upt func (db *Database) CreateUptimeReport(report *UptimeReport) error { return db.gormDB.Create(report).Error } - -// Consumption updates the consumption report for a specific node -func (db *Database) Consumption(nodeID uint64, report Consumption) (err error) { - var node Node - - if err := db.gormDB.First(&node, nodeID).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return ErrRecordNotFound - } - return err - } - - node.Consumption = report - if result := db.gormDB.Save(&node); result.Error != nil { - return result.Error - } - return nil -} diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index b345d9a..63b8ed7 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -1,10 +1,7 @@ package server import ( - "crypto/ed25519" - "crypto/sha256" "encoding/base64" - "encoding/hex" "errors" "fmt" "net/http" @@ -12,21 +9,28 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/lib/pq" "github.com/threefoldtech/tfgrid-sdk-go/node-registrar/pkg/db" ) -// @Summary list farms -// @Description list farms with specific filter -// @Accept json -// @Produce json -// @Param farm_name query string false "farm name" -// @Param farm_id query uint64 false "farm id" -// @Param twin_id query uint64 false "twin id" -// @Param page query int false "Page number" -// @Param size query int false "Max result per page" -// @Success 200 {object} []db.Farm -// @Failure 400 {object} error -// @Router /farms/ [get] +const ( + PubKeySize = 32 + MaxTimestampDelta = 2 * time.Second +) + +// @Summary List farms +// @Description Get a list of farms with optional filters +// @Tags farms +// @Accept json +// @Produce json +// @Param farm_name query string false "Filter by farm name" +// @Param farm_id query int false "Filter by farm ID" +// @Param twin_id query int false "Filter by twin ID" +// @Param page query int false "Page number" default(1) +// @Param size query int false "Results per page" default(10) +// @Success 200 {object} gin.H "List of farms" +// @Failure 400 {object} gin.H "Bad request" +// @Router /farms [get] func (s Server) listFarmsHandler(c *gin.Context) { var filter db.FarmFilter limit := db.DefaultLimit() @@ -48,15 +52,16 @@ func (s Server) listFarmsHandler(c *gin.Context) { }) } -// @Summary get farm -// @Description get a farm with specific id -// @Accept json -// @Produce json -// @param farm_id path uint64 true "farm id" -// @Success 200 {object} db.Farm -// @Failure 404 {object} db.ErrRecordNotFound -// @Failure 400 {object} error -// @Router /farm/{farm_id} [get] +// @Summary Get farm details +// @Description Get details for a specific farm +// @Tags farms +// @Accept json +// @Produce json +// @Param farm_id path int true "Farm ID" +// @Success 200 {object} gin.H "Farm details" +// @Failure 400 {object} gin.H "Invalid farm ID" +// @Failure 404 {object} gin.H "Farm not found" +// @Router /farms/{farm_id} [get] func (s Server) getFarmHandler(c *gin.Context) { farmID := c.Param("farm_id") @@ -82,19 +87,16 @@ func (s Server) getFarmHandler(c *gin.Context) { }) } -// @Summary create a farm -// @Description creates a farm -// @Accept json -// @Produce json -// @Param farm_id body uint64 true "farm id" -// @Param farm_name body uint64 true "farm name" -// @Param twin_id body uint64 true "twin id" -// @Param dedicated body bool false "dedicated farm" -// @Param farm_free_ips body uint64 false "farm free ips" -// @Success 200 {object} db.Farm -// @Failure 400 {object} error -// @Failure 409 {object} db.ErrRecordAlreadyExists -// @Router /farms/ [post] +// @Summary Create new farm +// @Description Create a new farm entry +// @Tags farms +// @Accept json +// @Produce json +// @Param farm body db.Farm true "Farm creation data" +// @Success 201 {object} gin.H "Farm created successfully" +// @Failure 400 {object} gin.H "Invalid request" +// @Failure 409 {object} gin.H "Farm already exists" +// @Router /farms [post] func (s Server) createFarmHandler(c *gin.Context) { var farm db.Farm @@ -103,6 +105,11 @@ func (s Server) createFarmHandler(c *gin.Context) { return } + ensureOwner(c, farm.TwinID) + if c.IsAborted() { + return + } + err := s.db.CreateFarm(farm) if err != nil { status := http.StatusBadRequest @@ -121,21 +128,23 @@ func (s Server) createFarmHandler(c *gin.Context) { }) } -// @Summary update a farm -// @Description update a farm -// @Accept json -// @Produce json -// @Param farm_id body uint64 true "farm id" -// @Param farm_name body uint64 false "farm name" -// @Param twin_id body uint64 false "twin id" -// @Param dedicated body bool false "dedicated farm" -// @Param farm_free_ips body uint64 false "farm free ips" -// @Success 200 {object} db.Farm -// @Failure 400 {object} error -// @Failure 404 {object} db.ErrRecordNotFound -// @Router /farms/ [patch] +type UpdateFarmRequest struct { + FarmName string `json:"farm_name" binding:"required,min=1,max=40"` +} + +// @Summary Update farm +// @Description Update existing farm details +// @Tags farms +// @Accept json +// @Produce json +// @Param farm_id path int true "Farm ID" +// @Param request body UpdateFarmRequest true "Farm update data" +// @Success 200 {object} gin.H "Farm updated successfully" +// @Failure 400 {object} gin.H "Invalid request" +// @Failure 404 {object} gin.H "Farm not found" +// @Router /farms/{farm_id} [patch] func (s Server) updateFarmsHandler(c *gin.Context) { - var farm db.Farm + var req UpdateFarmRequest farmID := c.Param("farm_id") id, err := strconv.ParseUint(farmID, 10, 64) @@ -144,47 +153,61 @@ func (s Server) updateFarmsHandler(c *gin.Context) { return } - if err := c.ShouldBindJSON(&farm); err != nil { + if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("failed to parse farm info: %v", err.Error())}) return } - if farm.FarmID != 0 && farm.FarmID != id { - c.JSON(http.StatusBadRequest, gin.H{"error": "farm id does not match farm id in the request"}) - return - } - - err = s.db.UpdateFarm(id, farm) + existingFarm, err := s.db.GetFarm(id) if err != nil { - status := http.StatusBadRequest - if errors.Is(err, db.ErrRecordNotFound) { - status = http.StatusNotFound + c.JSON(http.StatusNotFound, gin.H{"error": "Farm not found"}) + return } + c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"}) + return + } - c.JSON(status, gin.H{"error": err.Error()}) + ensureOwner(c, existingFarm.TwinID) + if c.IsAborted() { return } + // No need to hit DB if new farm name is same as the old one + if existingFarm.FarmName != req.FarmName { + err = s.db.UpdateFarm(id, req.FarmName) + if err != nil { + status := http.StatusBadRequest + + if errors.Is(err, db.ErrRecordNotFound) { + status = http.StatusNotFound + } + + c.JSON(status, gin.H{"error": err.Error()}) + return + } + } + c.JSON(http.StatusOK, gin.H{ "message": "Farm was updated successfully", }) } -// @Summary list nodes -// @Description list nodes with specific filter -// @Accept json -// @Produce json -// @Param node_id query uint64 false "node id" -// @Param farm_id query uint64 false "farm id" -// @Param twin_id query uint64 false "twin id" -// @Param status query string false "node status" -// @Param healthy query bool false "is node healthy" -// @Param page query int false "Page number" -// @Param size query int false "Max result per page" -// @Success 200 {object} []db.Node -// @Failure 400 {object} error -// @Router /nodes/ [get] +// @Summary List nodes +// @Description Get a list of nodes with optional filters +// @Tags nodes +// @Accept json +// @Produce json +// @Param node_id query int false "Filter by node ID" +// @Param farm_id query int false "Filter by farm ID" +// @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 page query int false "Page number" default(1) +// @Param size query int false "Results per page" default(10) +// @Success 200 {object} gin.H "List of nodes" +// @Failure 400 {object} gin.H "Bad request" +// @Router /nodes [get] func (s Server) listNodesHandler(c *gin.Context) { var filter db.NodeFilter limit := db.DefaultLimit() @@ -207,14 +230,15 @@ func (s Server) listNodesHandler(c *gin.Context) { }) } -// @Summary get node -// @Description get a node with specific id -// @Accept json -// @Produce json -// @param node_id path uint64 false "node id" -// @Success 200 {object} db.Node -// @Failure 404 {object} db.ErrRecordNotFound -// @Failure 400 {object} error +// @Summary Get node details +// @Description Get details for a specific node +// @Tags nodes +// @Accept json +// @Produce json +// @Param node_id path int true "Node ID" +// @Success 200 {object} gin.H "Node details" +// @Failure 400 {object} gin.H "Invalid node ID" +// @Failure 404 {object} gin.H "Node not found" // @Router /nodes/{node_id} [get] func (s Server) getNodeHandler(c *gin.Context) { nodeID := c.Param("node_id") @@ -241,34 +265,52 @@ func (s Server) getNodeHandler(c *gin.Context) { }) } -// @Summary register a node -// @Description register a node -// @Accept json -// @Produce json -// @Param node_id body uint64 true "node id" -// @Param farm_id body uint64 true "farm id" -// @Param twin_id body uint64 true "twin id" -// @Param features body []string true "node features " -// @Param status body string false "node status" -// @Param healthy body bool false "node healthy" -// @Param dedicated body bool false "node dedicated" -// @Param rented body bool false "node rented" -// @Param rentable body bool false "node rentable" -// @Param price_usd body float64 false "price in usd" -// @Param uptime body db.Uptime false "uptime report" -// @Param consumption body db.Consumption false "consumption report" -// @Success 200 {object} db.Node -// @Failure 400 {object} error -// @Failure 409 {object} db.ErrRecordAlreadyExists -// @Router /nodes/ [post] +type NodeRegistrationRequest struct { + TwinID uint64 `json:"twin_id" binding:"required,min=1"` + FarmID uint64 `json:"farm_id" binding:"required,min=1"` + Resources db.Resources `json:"resources" binding:"required"` + Location db.Location `json:"location" binding:"required"` + Interfaces []db.Interface `json:"interfaces" binding:"required,min=1,dive"` + SecureBoot bool `json:"secure_boot"` + Virtualized bool `json:"virtualized"` + SerialNumber string `json:"serial_number" binding:"required"` +} + +// @Summary Register new node +// @Description Register a new node in the system +// @Tags nodes +// @Accept json +// @Produce json +// @Param request body NodeRegistrationRequest true "Node registration data" +// @Success 201 {object} gin.H "Node registered successfully" +// @Failure 400 {object} gin.H "Invalid request" +// @Failure 409 {object} gin.H "Node already exists" +// @Router /nodes [post] func (s Server) registerNodeHandler(c *gin.Context) { - var node db.Node + var req NodeRegistrationRequest - if err := c.ShouldBindJSON(&node); err != nil { + if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } + ensureOwner(c, req.TwinID) + if c.IsAborted() { + return + } + + node := db.Node{ + TwinID: req.TwinID, + FarmID: req.FarmID, + Resources: req.Resources, + Location: req.Location, + Interfaces: req.Interfaces, + SecureBoot: req.SecureBoot, + Virtualized: req.Virtualized, + SerialNumber: req.SerialNumber, + Approved: false, // Default to unapproved awaiting farmer approval + } + err := s.db.RegisterNode(node) if err != nil { status := http.StatusBadRequest @@ -293,15 +335,16 @@ type UptimeReportRequest struct { Timestamp time.Time `json:"timestamp" binding:"required"` } -// @Summary uptime report -// @Description save uptime report of a node -// @Accept json -// @Produce json -// @Param node_id path uint64 true "node id" -// @Param request body UptimeReportRequest true "uptime report request" -// @Success 201 {object} map[string]string "message: uptime reported successfully" -// @Failure 400 {object} map[string]string "error: error message" -// @Failure 404 {object} map[string]string "error: node not found" +// @Summary Report node uptime +// @Description Submit uptime report for a node +// @Tags nodes +// @Accept json +// @Produce json +// @Param node_id path int true "Node ID" +// @Param request body UptimeReportRequest true "Uptime report data" +// @Success 201 {object} gin.H "Uptime reported successfully" +// @Failure 400 {object} gin.H "Invalid request" +// @Failure 404 {object} gin.H "Node not found" // @Router /nodes/{node_id}/uptime [post] func (s *Server) uptimeReportHandler(c *gin.Context) { var req UptimeReportRequest @@ -310,13 +353,17 @@ func (s *Server) uptimeReportHandler(c *gin.Context) { return } - // Get node and last report - _, err := s.db.GetNode(req.NodeID) + // Get node + node, err := s.db.GetNode(req.NodeID) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "node not found"}) return } + ensureOwner(c, node.TwinID) + if c.IsAborted() { + return + } // Detect restarts // Validate report timing (40min ± 5min window) // Maybe aggregate reports here and store total uptime? @@ -338,49 +385,6 @@ func (s *Server) uptimeReportHandler(c *gin.Context) { c.JSON(http.StatusCreated, gin.H{"message": "uptime reported successfully"}) } -// @Summary consumption report -// @Description save consumption report of a node -// @Accept json -// @Produce json -// @Param node_id query uint64 true "node id" -// @Param consumption body db.Consumption false "consumption report" -// @Success 200 {object} string -// @Failure 400 {object} error -// @Failure 404 {object} db.ErrRecordNotFound -// @Router /nodes/{node_id}/uptime [post] -func (s Server) storeConsumptionHandler(c *gin.Context) { - nodeID := c.Param("node_id") - - id, err := strconv.ParseUint(nodeID, 10, 64) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid node id"}) - return - } - - var consumption db.Consumption - - if err := c.ShouldBindJSON(&consumption); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - err = s.db.Consumption(id, consumption) - if err != nil { - status := http.StatusBadRequest - - if errors.Is(err, db.ErrRecordNotFound) { - status = http.StatusNotFound - } - - c.JSON(status, gin.H{"error": err.Error()}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "message": "consumption report received successfully", - }) -} - func parseQueryParams(c *gin.Context, types_ ...interface{}) error { for _, type_ := range types_ { if err := c.ShouldBindQuery(type_); err != nil { @@ -392,40 +396,25 @@ func parseQueryParams(c *gin.Context, types_ ...interface{}) error { // AccountRequest represents the request body for account operations type AccountCreationRequest struct { - PublicKey string `json:"public_key" binding:"required"` + Timestamp int64 `json:"timestamp" binding:"required"` + PublicKey string `json:"public_key" binding:"required"` // base64 encoded + // the registrar expect a signature of a message with format `timestampStr:publicKeyBase64` + // - signature format: base64(ed25519_or_sr22519_signature) Signature string `json:"signature" binding:"required"` - Timestamp int64 `json:"timestamp" binding:"required"` Relays []string `json:"relays,omitempty"` RMBEncKey string `json:"rmb_enc_key,omitempty"` } -const ( - MaxTimestampDelta = 2 * time.Second -) - -// Challenge is uniquely tied to both the timestamp and public key -// Prevents replay attacks across different accounts, still no state management required -// createChallenge creates a deterministic challenge from timestamp and public key -func createChallenge(timestamp int64, publicKey string) string { - // Create a unique message combining action, timestamp, and public key - message := fmt.Sprintf("create_account:%d:%s", timestamp, publicKey) - - // Hash the message - hash := sha256.Sum256([]byte(message)) - return hex.EncodeToString(hash[:]) -} - -// @Summary creates a new account/twin -// @Description creates a new account after verifying key ownership -// @Accept json -// @Produce json -// @Param public_key body string true "base64 encoded public key" -// @Param signature body string true "base64 encoded signature" -// @Param timestamp body uint64 true "timestamp" -// @Success 201 {object} db.Account -// @Failure 400 {object} error -// @Failure 409 {object} db.ErrRecordAlreadyExists -// @Router /accounts/ [post] +// @Summary Create new account +// @Description Create a new twin account with cryptographic verification +// @Tags accounts +// @Accept json +// @Produce json +// @Param request body AccountCreationRequest true "Account creation data" +// @Success 201 {object} db.Account "Created account details" +// @Failure 400 {object} gin.H "Invalid request" +// @Failure 409 {object} gin.H "Account already exists" +// @Router /accounts [post] func (s *Server) createAccountHandler(c *gin.Context) { var req AccountCreationRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -453,17 +442,24 @@ func (s *Server) createAccountHandler(c *gin.Context) { } // Create challenge using timestamp and public key - challenge := createChallenge(req.Timestamp, req.PublicKey) + // Challenge is uniquely tied to both the timestamp and public key + // Prevents replay attacks, still no state management required + challenge := []byte(fmt.Sprintf("%d:%s", req.Timestamp, req.PublicKey)) - // Verify signature of the challenge - valid, err := verifySignature(req.PublicKey, challenge, req.Signature) + // Decode public key from base64 + publicKeyBytes, err := base64.StdEncoding.DecodeString(req.PublicKey) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("signature verification error: %v", err)}) - return + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid public key format"}) } - - if !valid { - c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid signature"}) + // Decode signature from base64 + signatureBytes, err := base64.StdEncoding.DecodeString(req.Signature) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("invalid signature format: %v", err)}) + } + // Verify signature of the challenge + err = verifySignature(publicKeyBytes, challenge, signatureBytes) + if err != nil { + c.JSON(http.StatusUnauthorized, gin.H{"error": fmt.Sprintf("signature verification error: %v", err)}) return } @@ -484,9 +480,16 @@ func (s *Server) createAccountHandler(c *gin.Context) { c.JSON(http.StatusCreated, account) } +/* // verifySignature verifies an ED25519 signature +func verifySignature(publicKey, chalange, signature []byte) (bool, error) { + + // Verify the signature + return ed25519.Verify(publicKey, chalange, signature), nil +} */ + type UpdateAccountRequest struct { - Relays []string `json:"relays"` - RMBEncKey string `json:"rmb_enc_key"` + Relays pq.StringArray `json:"relays"` + RMBEncKey string `json:"rmb_enc_key"` } // updateAccountHandler updates an account's relays and RMB encryption key @@ -504,23 +507,28 @@ type UpdateAccountRequest struct { func (s *Server) updateAccountHandler(c *gin.Context) { twinID, err := strconv.ParseUint(c.Param("twin_id"), 10, 64) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "invalid twin ID"}) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid twin ID"}) + return + } + + ensureOwner(c, twinID) + if c.IsAborted() { return } var req UpdateAccountRequest if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"}) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid request body"}) return } err = s.db.UpdateAccount(twinID, req.Relays, req.RMBEncKey) if err != nil { if errors.Is(err, db.ErrRecordNotFound) { - c.JSON(http.StatusNotFound, gin.H{"error": "account not found"}) + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": "account not found"}) return } - c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update account"}) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "failed to update account"}) return } @@ -558,35 +566,34 @@ func (s *Server) getAccountHandler(c *gin.Context) { c.JSON(http.StatusOK, account) } -// verifySignature verifies an ED25519 signature -func verifySignature(publicKeyBase64, message, signatureBase64 string) (bool, error) { - // Decode public key from base64 +// Helper function to validate public key format +func isValidPublicKey(publicKeyBase64 string) bool { publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64) if err != nil { - return false, fmt.Errorf("invalid public key format: %w", err) + return false } + return len(publicKeyBytes) == PubKeySize +} - // Verify public key length - if len(publicKeyBytes) != ed25519.PublicKeySize { - return false, fmt.Errorf("invalid public key size: expected %d, got %d", - ed25519.PublicKeySize, len(publicKeyBytes)) +// Helper function to ensure the request is from the owner +func ensureOwner(c *gin.Context, twinID uint64) { + // Retrieve twinID set by the authMiddleware + authTwinID, exists := c.Get("twinID") + if !exists { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "not authorized"}) + return } - // Decode signature from base64 - signatureBytes, err := base64.StdEncoding.DecodeString(signatureBase64) - if err != nil { - return false, fmt.Errorf("invalid signature format: %w", err) + // Safe type assertion + authID, ok := authTwinID.(uint64) + if !ok { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid authentication type"}) + return } - // Verify the signature - return ed25519.Verify(publicKeyBytes, []byte(message), signatureBytes), nil -} - -// Helper function to validate public key format -func isValidPublicKey(publicKeyBase64 string) bool { - publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64) - if err != nil { - return false + // Ensure that the retrieved twinID equals to the passed twinID + if authID != twinID || twinID == 0 { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "not authorized"}) + return } - return len(publicKeyBytes) == ed25519.PublicKeySize } diff --git a/node-registrar/pkg/server/middlewares.go b/node-registrar/pkg/server/middlewares.go new file mode 100644 index 0000000..0953c6f --- /dev/null +++ b/node-registrar/pkg/server/middlewares.go @@ -0,0 +1,124 @@ +package server + +import ( + "encoding/base64" + "errors" + "fmt" + "log" + "net/http" + "strconv" + "strings" + "time" + + "github.com/gin-gonic/gin" + "github.com/threefoldtech/tfgrid-sdk-go/node-registrar/pkg/db" +) + +const ( + AuthHeader = "X-Auth" + ChallengeValidity = 5 * time.Minute +) + +// AuthMiddleware is a middleware function that authenticates incoming requests based on the X-Auth header. +// It verifies the challenge and signature provided in the header against the account's public key stored in the database. +// If the authentication fails, it aborts the request with an appropriate error status and message. +// When authentication succeeds, it set the twinID in the contexct so handlers can trust that the requester is the owner of that Account/Twin. +// Authorization must be checked next independently by handlers or other middlewares. +// header format `Challenge:Signature` +// - chalange format: base64(message) where the message is `timestampStr:twinIDStr` +// - signature format: base64(ed25519_or_sr22519_signature) +// TODO: do we need to support both? Maybe if only ed25519 needed we can rely on crypto pkg instead of using go-subkey +func (s *Server) AuthMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + // Extract and validate headers + authHeader := c.GetHeader(AuthHeader) + if authHeader == "" { + abortWithError(c, http.StatusUnauthorized, "Authorization header required") + return + } + + parts := strings.Split(authHeader, ":") + if len(parts) != 2 { + abortWithError(c, http.StatusBadRequest, "Invalid header format. Use 'Challenge:Signature'") + return + } + + challengeB64, signatureB64 := parts[0], parts[1] + + // Decode and validate challenge + challenge, err := base64.StdEncoding.DecodeString(challengeB64) + if err != nil { + abortWithError(c, http.StatusBadRequest, "Invalid challenge encoding") + return + } + + challengeParts := strings.Split(string(challenge), ":") + if len(challengeParts) != 2 { + abortWithError(c, http.StatusBadRequest, "Invalid challenge format") + return + } + + timestampStr, twinIDStr := challengeParts[0], challengeParts[1] + + // Validate timestamp + timestamp, err := strconv.ParseInt(timestampStr, 10, 64) + if err != nil { + abortWithError(c, http.StatusBadRequest, "Invalid timestamp") + return + } + + if time.Since(time.Unix(timestamp, 0)) > ChallengeValidity { + abortWithError(c, http.StatusUnauthorized, "Expired challenge") + return + } + + // Verify signature (supports both ED25519 and SR25519) + twinID, err := strconv.ParseUint(twinIDStr, 10, 64) + if err != nil { + abortWithError(c, http.StatusBadRequest, "Invalid twin ID format") + return + } + + account, err := s.db.GetAccount(twinID) + if err != nil { + handleDatabaseError(c, err) + return + } + + storedPK, err := base64.StdEncoding.DecodeString(account.PublicKey) + if err != nil { + abortWithError(c, http.StatusBadRequest, fmt.Sprintf("invalid stored public key: %v", err)) + return + } + + sig, err := base64.StdEncoding.DecodeString(signatureB64) + if err != nil { + abortWithError(c, http.StatusBadRequest, "Invalid signature encoding") + return + } + + // Verify using substrate address and challenge + if err := verifySignature(storedPK, challenge, sig); err != nil { + abortWithError(c, http.StatusUnauthorized, fmt.Sprintf("Signature verification failed: %v", err)) + return + } + + // Store verified twin ID in context, must be checked form the handlers to ensure altred resources belongs to same user + c.Set("twinID", twinID) + log.Println(twinID) + c.Next() + } +} + +// Helper functions +func abortWithError(c *gin.Context, code int, msg string) { + c.AbortWithStatusJSON(code, gin.H{"error": msg}) +} + +func handleDatabaseError(c *gin.Context, err error) { + if errors.Is(err, db.ErrRecordNotFound) { + abortWithError(c, http.StatusNotFound, "Account not found") + } else { + abortWithError(c, http.StatusInternalServerError, "Database error") + } +} diff --git a/node-registrar/pkg/server/routes.go b/node-registrar/pkg/server/routes.go index eabd94a..f62e8a4 100644 --- a/node-registrar/pkg/server/routes.go +++ b/node-registrar/pkg/server/routes.go @@ -15,25 +15,25 @@ func (s *Server) SetupRoutes() { farmRoutes := v1.Group("farms") farmRoutes.GET("/", s.listFarmsHandler) farmRoutes.GET("/:farm_id", s.getFarmHandler) + // protected by farmer key + farmRoutes.Use(s.AuthMiddleware()) farmRoutes.POST("/", s.createFarmHandler) - - farmRoutes.Use(s.createAuthFarmMiddleware(s.network)) farmRoutes.PATCH("/:farm_id", s.updateFarmsHandler) // nodes routes nodeRoutes := v1.Group("nodes") nodeRoutes.GET("/", s.listNodesHandler) nodeRoutes.GET("/:node_id", s.getNodeHandler) + // protected by node key + nodeRoutes.Use(s.AuthMiddleware()) nodeRoutes.POST("/", s.registerNodeHandler) - - nodeRoutes.Use(s.createAuthNodeMiddleware(s.network)) nodeRoutes.POST("/:node_id/uptime", s.uptimeReportHandler) - nodeRoutes.POST("/:node_id/consumption", s.storeConsumptionHandler) // Account routes accountRoutes := v1.Group("accounts") accountRoutes.POST("/", s.createAccountHandler) accountRoutes.GET("/:twin_id", s.getAccountHandler) + // protected by farmer key + accountRoutes.Use(s.AuthMiddleware()) accountRoutes.PATCH("/:twin_id", s.updateAccountHandler) - } diff --git a/node-registrar/pkg/server/stacks.go b/node-registrar/pkg/server/stacks.go deleted file mode 100644 index f656eec..0000000 --- a/node-registrar/pkg/server/stacks.go +++ /dev/null @@ -1,148 +0,0 @@ -package server - -import ( - "encoding/base64" - "fmt" - "net/http" - "strconv" - "strings" - - "github.com/gin-gonic/gin" - "github.com/threefoldtech/tfgrid-sdk-go/grid-client/subi" -) - -var ( - DevNetwork = "dev" - QaNetwork = "qa" - TestNetwork = "test" - MainNetwork = "main" - - SubstrateURLs = map[string][]string{ - DevNetwork: { - "wss://tfchain.dev.grid.tf/ws", - "wss://tfchain.dev.grid.tf:443", - "wss://tfchain.02.dev.grid.tf/ws", - "wss://tfchain.02.dev.grid.tf:443", - }, - QaNetwork: { - "wss://tfchain.qa.grid.tf/ws", - "wss://tfchain.qa.grid.tf:443", - "wss://tfchain.02.qa.grid.tf/ws", - "wss://tfchain.02.qa.grid.tf:443", - }, - TestNetwork: { - "wss://tfchain.test.grid.tf/ws", - "wss://tfchain.test.grid.tf:443", - "wss://tfchain.02.test.grid.tf/ws", - "wss://tfchain.02.test.grid.tf:443", - }, - MainNetwork: { - "wss://tfchain.grid.tf/ws", - "wss://tfchain.grid.tf:443", - "wss://tfchain.02.grid.tf/ws", - "wss://tfchain.02.grid.tf:443", - }, - } -) - -func (server Server) createAuthFarmMiddleware(network string) gin.HandlerFunc { - return func(c *gin.Context) { - authHeader := c.GetHeader("Authorization") - if authHeader == "" { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Authorization header required"}) - return - } - - token := strings.TrimPrefix(authHeader, "Bearer ") - - pubKey, err := base64.StdEncoding.DecodeString(token) - if err != nil { - fmt.Println("Error decoding token:", err.Error()) - return - } - manager := subi.NewManager(SubstrateURLs[network]...) - - sub, err := manager.SubstrateExt() - if err != nil { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "could not get substrate client"}) - return - } - - twinID, err := sub.GetTwinByPubKey(pubKey) - if err != nil { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "failed to get twin id with public key"}) - return - } - - farmID := c.Param("farm_id") - - id, err := strconv.ParseUint(farmID, 10, 64) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Invalid farm_id: %v", err.Error())}) - return - } - - farm, err := server.db.GetFarm(id) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Invalid farm_id: %v", err.Error())}) - return - } - - if twinID != uint32(farm.TwinID) { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid or public key"}) - return - } - - c.Next() - } -} - -func (server Server) createAuthNodeMiddleware(network string) gin.HandlerFunc { - return func(c *gin.Context) { - authHeader := c.GetHeader("Authorization") - if authHeader == "" { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Authorization header required"}) - return - } - - token := strings.TrimPrefix(authHeader, "Bearer ") - pubKey, err := base64.StdEncoding.DecodeString(token) - if err != nil { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) - return - } - - manager := subi.NewManager(SubstrateURLs[network]...) - sub, err := manager.SubstrateExt() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - twinID, err := sub.GetTwinByPubKey(pubKey) - if err != nil { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) - return - } - - nodeID := c.Param("node_id") - id, err := strconv.ParseUint(nodeID, 10, 64) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - node, err := server.db.GetNode(id) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": err.Error()}) - return - } - - if twinID != uint32(node.TwinID) { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid twin id or public key"}) - return - } - - c.Next() - } -} diff --git a/node-registrar/pkg/server/util.go b/node-registrar/pkg/server/util.go new file mode 100644 index 0000000..3c31770 --- /dev/null +++ b/node-registrar/pkg/server/util.go @@ -0,0 +1,31 @@ +package server + +import ( + "errors" + "fmt" + + "github.com/vedhavyas/go-subkey/v2/ed25519" + "github.com/vedhavyas/go-subkey/v2/sr25519" +) + +func verifySignature(publicKey, challenge, signature []byte) error { + // Verify public key length + if len(publicKey) != PubKeySize { + return fmt.Errorf("invalid public key size: expected %d, got %d", + PubKeySize, len(publicKey)) + } + + // Try ED25519 verification first + edKey, err := ed25519.Scheme{}.FromPublicKey(publicKey) + if err == nil && edKey.Verify(challenge, signature) { + return nil + } + + // Fallback to SR25519 verification + srKey, err := sr25519.Scheme{}.FromPublicKey(publicKey) + if err == nil && srKey.Verify(challenge, signature) { + return nil + } + + return errors.New("signature verification failed") +} From c4f1ea9b161caa881b99cc77411034ed93e7e953 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Tue, 28 Jan 2025 11:13:19 +0200 Subject: [PATCH 08/31] use nodeID param in uptime handler instead of having it in the request --- node-registrar/pkg/server/handlers.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 63b8ed7..b1aca54 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -330,7 +330,6 @@ func (s Server) registerNodeHandler(c *gin.Context) { } type UptimeReportRequest struct { - NodeID uint64 `json:"node_id" binding:"required"` Uptime time.Duration `json:"uptime" binding:"required"` Timestamp time.Time `json:"timestamp" binding:"required"` } @@ -347,6 +346,14 @@ type UptimeReportRequest struct { // @Failure 404 {object} gin.H "Node not found" // @Router /nodes/{node_id}/uptime [post] func (s *Server) uptimeReportHandler(c *gin.Context) { + nodeID := c.Param("node_id") + + id, err := strconv.ParseUint(nodeID, 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid node id"}) + return + } + var req UptimeReportRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) @@ -354,7 +361,7 @@ func (s *Server) uptimeReportHandler(c *gin.Context) { } // Get node - node, err := s.db.GetNode(req.NodeID) + node, err := s.db.GetNode(id) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "node not found"}) return @@ -371,7 +378,7 @@ func (s *Server) uptimeReportHandler(c *gin.Context) { // Create report record report := &db.UptimeReport{ - NodeID: req.NodeID, + NodeID: id, Duration: req.Uptime, Timestamp: req.Timestamp, } From c522cfb9d05db15219d3d74740b38864f15cad3c Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Tue, 28 Jan 2025 12:25:01 +0200 Subject: [PATCH 09/31] removing FarmFreeIps for now --- node-registrar/pkg/db/models.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index 6aaa592..632044b 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -23,13 +23,12 @@ type Account struct { } type Farm struct { - FarmID uint64 `gorm:"primaryKey;autoIncrement" json:"farm_id"` - FarmName string `gorm:"size:40;not null;unique;check:farm_name <> ''" json:"farm_name"` - TwinID uint64 `json:"twin_id" gorm:"not null;check:twin_id > 0"` // Farmer account reference - Dedicated bool `json:"dedicated"` - FarmFreeIps uint64 `json:"farm_free_ips"` - CreatedAt time.Time - UpdatedAt time.Time + FarmID uint64 `gorm:"primaryKey;autoIncrement" json:"farm_id"` + FarmName string `gorm:"size:40;not null;unique;check:farm_name <> ''" json:"farm_name"` + TwinID uint64 `json:"twin_id" gorm:"not null;check:twin_id > 0"` // Farmer account reference + Dedicated bool `json:"dedicated"` + CreatedAt time.Time + UpdatedAt time.Time Nodes []Node `gorm:"foreignKey:FarmID;references:FarmID;constraint:OnDelete:RESTRICT" json:"nodes"` } From 02c6eb245a77bf801df96f9cb2cb1aeebe4d3395 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Tue, 28 Jan 2025 12:48:10 +0200 Subject: [PATCH 10/31] fix RegisterNode --- node-registrar/pkg/db/farms.go | 8 ++++---- node-registrar/pkg/db/models.go | 2 +- node-registrar/pkg/db/nodes.go | 15 ++++----------- node-registrar/pkg/server/handlers.go | 8 ++++---- node-registrar/pkg/server/middlewares.go | 3 +-- 5 files changed, 14 insertions(+), 22 deletions(-) diff --git a/node-registrar/pkg/db/farms.go b/node-registrar/pkg/db/farms.go index 014f671..e22dac4 100644 --- a/node-registrar/pkg/db/farms.go +++ b/node-registrar/pkg/db/farms.go @@ -39,14 +39,14 @@ func (db *Database) GetFarm(farmID uint64) (farm Farm, err error) { return } -func (db *Database) CreateFarm(farm Farm) (err error) { - if err = db.gormDB.Create(&farm).Error; err != nil { +func (db *Database) CreateFarm(farm Farm) (uint64, error) { + if err := db.gormDB.Create(&farm).Error; err != nil { if errors.Is(err, gorm.ErrDuplicatedKey) { - return ErrRecordAlreadyExists + return 0, ErrRecordAlreadyExists } } - return + return farm.FarmID, nil } func (db *Database) UpdateFarm(farmID uint64, name string) (err error) { diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index 632044b..b6114e2 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -37,7 +37,7 @@ type Node struct { NodeID uint64 `json:"node_id" gorm:"primaryKey;autoIncrement"` // Constraints set to prevents unintended account deletion if linked Farms/nodes exist. FarmID uint64 `json:"farm_id" gorm:"not null;check:farm_id> 0;foreignKey:FarmID;references:FarmID;constraint:OnDelete:RESTRICT"` - TwinID uint64 `json:"twin_id" gorm:"not null;check:twin_id > 0;foreignKey:TwinID;references:TwinID;constraint:OnDelete:RESTRICT"` // Node account reference + TwinID uint64 `json:"twin_id" gorm:"not null;unique;check:twin_id > 0;foreignKey:TwinID;references:TwinID;constraint:OnDelete:RESTRICT"` // Node account reference Location Location `json:"location" gorm:"not null;type:json"` diff --git a/node-registrar/pkg/db/nodes.go b/node-registrar/pkg/db/nodes.go index c1f244d..356f5cc 100644 --- a/node-registrar/pkg/db/nodes.go +++ b/node-registrar/pkg/db/nodes.go @@ -43,21 +43,14 @@ func (db *Database) GetNode(nodeID uint64) (node Node, err error) { } // RegisterNode registers a new node in the database -func (db *Database) RegisterNode(node Node) (err error) { - if result := db.gormDB.First(&node, node.NodeID); result.Error != nil { - if errors.Is(result.Error, gorm.ErrRecordNotFound) { - return ErrRecordNotFound - } - return result.Error - } - +func (db *Database) RegisterNode(node Node) (uint64, error) { if result := db.gormDB.Create(&node); result.Error != nil { if errors.Is(result.Error, gorm.ErrDuplicatedKey) { - return ErrRecordAlreadyExists + return 0, ErrRecordAlreadyExists } - return result.Error + return 0, result.Error } - return nil + return node.NodeID, nil } // Uptime updates the uptime for a specific node diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index b1aca54..3471223 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -110,7 +110,7 @@ func (s Server) createFarmHandler(c *gin.Context) { return } - err := s.db.CreateFarm(farm) + farmID, err := s.db.CreateFarm(farm) if err != nil { status := http.StatusBadRequest @@ -124,7 +124,7 @@ func (s Server) createFarmHandler(c *gin.Context) { c.JSON(http.StatusCreated, gin.H{ "message": "Farm created successfully", - "farm": farm, + "farm_id": farmID, }) } @@ -311,7 +311,7 @@ func (s Server) registerNodeHandler(c *gin.Context) { Approved: false, // Default to unapproved awaiting farmer approval } - err := s.db.RegisterNode(node) + nodeID, err := s.db.RegisterNode(node) if err != nil { status := http.StatusBadRequest @@ -325,7 +325,7 @@ func (s Server) registerNodeHandler(c *gin.Context) { c.JSON(http.StatusCreated, gin.H{ "message": "node registered successfully", - "node": node, + "node_id": nodeID, }) } diff --git a/node-registrar/pkg/server/middlewares.go b/node-registrar/pkg/server/middlewares.go index 0953c6f..a5150a3 100644 --- a/node-registrar/pkg/server/middlewares.go +++ b/node-registrar/pkg/server/middlewares.go @@ -4,7 +4,6 @@ import ( "encoding/base64" "errors" "fmt" - "log" "net/http" "strconv" "strings" @@ -89,6 +88,7 @@ func (s *Server) AuthMiddleware() gin.HandlerFunc { if err != nil { abortWithError(c, http.StatusBadRequest, fmt.Sprintf("invalid stored public key: %v", err)) return + // Store verified twin ID in context, must be checked form the handlers to ensure altred resources belongs to same user } sig, err := base64.StdEncoding.DecodeString(signatureB64) @@ -105,7 +105,6 @@ func (s *Server) AuthMiddleware() gin.HandlerFunc { // Store verified twin ID in context, must be checked form the handlers to ensure altred resources belongs to same user c.Set("twinID", twinID) - log.Println(twinID) c.Next() } } From e94fc1ec3ec832f954167896bec547a21404f8a4 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Tue, 28 Jan 2025 13:50:54 +0200 Subject: [PATCH 11/31] revert to go 1.21.0 --- node-registrar/go.mod | 61 +++++++++---------- node-registrar/go.sum | 137 ++++++++++++++++++++++-------------------- 2 files changed, 102 insertions(+), 96 deletions(-) diff --git a/node-registrar/go.mod b/node-registrar/go.mod index a3689b6..3ba5c67 100644 --- a/node-registrar/go.mod +++ b/node-registrar/go.mod @@ -1,11 +1,11 @@ module github.com/threefoldtech/tfgrid-sdk-go/node-registrar -go 1.22.0 +go 1.21 -toolchain go1.22.1 +toolchain go1.21.0 require ( - github.com/gin-gonic/gin v1.9.0 + github.com/gin-gonic/gin v1.10.0 github.com/lib/pq v1.10.9 github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.33.0 @@ -13,23 +13,23 @@ require ( github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/swag v1.16.4 github.com/vedhavyas/go-subkey/v2 v2.0.0 - gorm.io/driver/postgres v1.5.7 - gorm.io/gorm v1.25.10 + gorm.io/driver/postgres v1.5.11 + gorm.io/gorm v1.25.12 ) require ( - github.com/ChainSafe/go-schnorrkel v1.1.0 // indirect + github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/bytedance/sonic v1.8.0 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/decred/base58 v1.0.5 // indirect - github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect - github.com/ethereum/go-ethereum v1.11.6 // indirect + github.com/decred/base58 v1.0.4 // indirect + github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.6 // indirect @@ -37,11 +37,10 @@ require ( github.com/go-openapi/swag v0.19.15 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.11.2 // indirect - github.com/goccy/go-json v0.10.0 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect - github.com/holiman/uint256 v1.2.3 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.5.5 // indirect @@ -50,28 +49,26 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.0.9 // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/leodido/go-urn v1.2.1 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect - github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/stretchr/testify v1.10.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.9 // indirect - golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect - golang.org/x/crypto v0.32.0 // indirect - golang.org/x/net v0.34.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/text v0.21.0 // indirect - golang.org/x/tools v0.29.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.22.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/node-registrar/go.sum b/node-registrar/go.sum index 762015e..8e163d0 100644 --- a/node-registrar/go.sum +++ b/node-registrar/go.sum @@ -1,5 +1,5 @@ -github.com/ChainSafe/go-schnorrkel v1.1.0 h1:rZ6EU+CZFCjB4sHUE1jIu8VDoB/wRKZxoe1tkcO71Wk= -github.com/ChainSafe/go-schnorrkel v1.1.0/go.mod h1:ABkENxiP+cvjFiByMIZ9LYbRoNNLeBLiakC1XeTFxfE= +github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRrLeDnvGIM= +github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= @@ -8,34 +8,38 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= -github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA= -github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= -github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/base58 v1.0.5 h1:hwcieUM3pfPnE/6p3J100zoRfGkQxBulZHo7GZfOqic= -github.com/decred/base58 v1.0.5/go.mod h1:s/8lukEHFA6bUQQb/v3rjUySJ2hu+RioCzLukAVkrfw= -github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= -github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/ethereum/go-ethereum v1.11.6 h1:2VF8Mf7XiSUfmoNOy3D+ocfl9Qu8baQBrCNbo2CXQ8E= -github.com/ethereum/go-ethereum v1.11.6/go.mod h1:+a8pUj1tOyJ2RinsNQD4326YS+leSoKGiG/uVVb0x6Y= +github.com/decred/base58 v1.0.4 h1:QJC6B0E0rXOPA8U/kw2rP+qiRJsUaE2Er+pYb3siUeA= +github.com/decred/base58 v1.0.4/go.mod h1:jJswKPEdvpFpvf7dsDvFZyLT22xZ9lWqEByX38oGd9E= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/ethereum/go-ethereum v1.10.20 h1:75IW830ClSS40yrQC1ZCMZCt5I+zU16oqId2SiQwdQ4= +github.com/ethereum/go-ethereum v1.10.20/go.mod h1:LWUN82TCHGpxB3En5HVmLLzPD7YSrEUFmFfN1nKkVN0= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= -github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -52,20 +56,19 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= -github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= -github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= -github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= -github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= -github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= @@ -82,17 +85,19 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -108,36 +113,37 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b h1:QrHweqAtyJ9EwCaGHBu1fghwxIPiopAHV06JlXrMHjk= github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b/go.mod h1:xxLb2ip6sSUts3g1irPVHyk/DGslwQsNOo9I7smJfNU= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= -github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= @@ -146,33 +152,35 @@ github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A= github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU= -github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/vedhavyas/go-subkey/v2 v2.0.0 h1:LemDIsrVtRSOkp0FA8HxP6ynfKjeOj3BY2U9UNfeDMA= github.com/vedhavyas/go-subkey/v2 v2.0.0/go.mod h1:95aZ+XDCWAUUynjlmi7BtPExjXgXxByE0WfBwbmIRH4= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -184,8 +192,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -194,16 +202,16 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -216,8 +224,9 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM= -gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA= -gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= -gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314= +gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= +gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= +gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= From ab8214cf43474532d951c6e382d7cf6989bdbbdc Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Tue, 28 Jan 2025 14:12:18 +0200 Subject: [PATCH 12/31] update comments --- node-registrar/main_.old | 331 +++++++++++++++++++++++ node-registrar/pkg/server/middlewares.go | 4 +- 2 files changed, 332 insertions(+), 3 deletions(-) create mode 100644 node-registrar/main_.old diff --git a/node-registrar/main_.old b/node-registrar/main_.old new file mode 100644 index 0000000..2418d00 --- /dev/null +++ b/node-registrar/main_.old @@ -0,0 +1,331 @@ +package main + +import ( + "bytes" + "crypto/ed25519" + "crypto/rand" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "strings" + "time" +) + +// Example for creating an account +func createAccountExample() (ed25519.PrivateKey, uint64, error) { + // Generate key pair + publicKey, privateKey, _ := ed25519.GenerateKey(nil) + + // Prepare request payload + timestamp := time.Now().Unix() + challenge := []byte(fmt.Sprintf("%d:%s", timestamp, base64.StdEncoding.EncodeToString(publicKey))) + signature := ed25519.Sign(privateKey, challenge) + + reqBody := fmt.Sprintf(`{ + "timestamp": %d, + "public_key": "%s", + "signature": "%s" + }`, + timestamp, + base64.StdEncoding.EncodeToString(publicKey), + base64.StdEncoding.EncodeToString(signature), + ) + + // Send request + resp, err := http.Post( + "http://localhost:8080/v1/accounts", + "application/json", + strings.NewReader(reqBody), + ) + if err != nil { + fmt.Println("Error sending request:", err) + return nil, 0, err + } + var twinID uint64 + if resp != nil { + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println("Error reading response body:", err) + } else { + fmt.Println("Response received:", string(body)) + var account map[string]interface{} + err = json.Unmarshal(body, &account) + if err != nil { + fmt.Println("Error unmarshalling response body:", err) + return nil, 0, err + } + twinID = uint64(account["TwinID"].(float64)) + } + } else { + fmt.Println("No response received") + } + + fmt.Println("TwinID:", twinID) + return privateKey, twinID, nil +} + +// Example for authenticating to access protected endpoint (e.g., update account) +func authenticatedRequestExample(twinID uint64, privateKey ed25519.PrivateKey) { + client := &http.Client{} + + // Create authentication challenge + timestamp := time.Now().Unix() + challenge := []byte(fmt.Sprintf("%d:%v", timestamp, twinID)) + signature := ed25519.Sign(privateKey, challenge) + + // Create request + req, _ := http.NewRequest( + "PATCH", + fmt.Sprintf("http://localhost:8080/v1/accounts/%v", twinID), + strings.NewReader(`{"relays": ["relay.example.com"], "rmb_enc_key": "abc123"}`), + ) + + // Set auth header + authHeader := fmt.Sprintf( + "%s:%s", + base64.StdEncoding.EncodeToString(challenge), + base64.StdEncoding.EncodeToString(signature), + ) + req.Header.Set("X-Auth", authHeader) + req.Header.Set("Content-Type", "application/json") + + // Send request + resp, err := client.Do(req) + if err != nil { + fmt.Println("Error sending request:", err) + return + } + + if resp != nil { + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println("Error reading response body:", err) + } else { + fmt.Println("Response received:", string(body)) + } + } else { + fmt.Println("No response received") + } +} + +// Get account +func getAccountExample(twinID uint64) { + client := &http.Client{} + + // Create request + req, _ := http.NewRequest( + "GET", + fmt.Sprintf("http://localhost:8080/v1/accounts/%v", twinID), + strings.NewReader(""), + ) + + // Send request + resp, err := client.Do(req) + if err != nil { + fmt.Println("Error sending request:", err) + return + } + + if resp != nil { + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println("Error reading response body:", err) + } else { + fmt.Println("Response received:", string(body)) + } + } else { + fmt.Println("No response received") + } +} + +func createFarmExample(twinID uint64, privateKey ed25519.PrivateKey) (uint64, error) { + client := &http.Client{} + randString := func(length int) string { + const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + result := make([]byte, length) + rand.Read(result) + for i, b := range result { + result[i] = chars[b%byte(len(chars))] + } + return string(result) + } + // Prepare farm payload + farmData := struct { + Name string `json:"farm_name"` + TwinID uint64 `json:"twin_id"` + }{ + Name: randString(20), + TwinID: twinID, + } + + body, _ := json.Marshal(farmData) + + // Create auth headers + timestamp := time.Now().Unix() + challenge := []byte(fmt.Sprintf("%d:%v", timestamp, twinID)) + signature := ed25519.Sign(privateKey, challenge) + + req, _ := http.NewRequest("POST", "http://localhost:8080/v1/farms", bytes.NewReader(body)) + + // Set required headers + authHeader := fmt.Sprintf( + "%s:%s", + base64.StdEncoding.EncodeToString(challenge), + base64.StdEncoding.EncodeToString(signature), + ) + req.Header.Set("X-Auth", authHeader) + req.Header.Set("Content-Type", "application/json") + + resp, err := client.Do(req) + if err != nil { + fmt.Println("Error: ", err) + return 0, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + body, _ := io.ReadAll(resp.Body) + fmt.Println("farm creation failed: ", string(body)) + return 0, err + } + + // Parse response + var result struct { + FarmID uint64 `json:"farm_id"` + } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + fmt.Println("Error: ", err) + return 0, err + } + + return result.FarmID, nil +} + +func registerNodeExample(twinID uint64, farmID uint64, privateKey ed25519.PrivateKey) error { + client := &http.Client{} + + // Prepare node payload matching server's NodeRegistrationRequest + nodeData := struct { + TwinID uint64 `json:"twin_id"` + FarmID uint64 `json:"farm_id"` + Resources struct { + CRU uint64 `json:"cru"` + SRU uint64 `json:"sru"` + HRU uint64 `json:"hru"` + MRU uint64 `json:"mru"` + } `json:"resources"` + Location struct { + Country string `json:"country"` + City string `json:"city"` + Longitude string `json:"longitude"` + Latitude string `json:"latitude"` + } `json:"location"` + Interfaces []struct { + Name string `json:"name"` + Mac string `json:"mac"` + IPs string `json:"ips"` + } `json:"interfaces"` + SecureBoot bool `json:"secure_boot"` + Virtualized bool `json:"virtualized"` + SerialNumber string `json:"serial_number"` + }{ + TwinID: twinID, + FarmID: farmID, + Resources: struct { + CRU uint64 `json:"cru"` + SRU uint64 `json:"sru"` + HRU uint64 `json:"hru"` + MRU uint64 `json:"mru"` + }{CRU: 4, SRU: 512, HRU: 1024, MRU: 2048}, + Location: struct { + Country string `json:"country"` + City string `json:"city"` + Longitude string `json:"longitude"` + Latitude string `json:"latitude"` + }{ + Country: "US", + City: "NY", + Longitude: "-74.005974", + Latitude: "40.712776", + }, + Interfaces: []struct { + Name string `json:"name"` + Mac string `json:"mac"` + IPs string `json:"ips"` + }{ + { + Name: "eth0", + Mac: "00:11:22:33:44:55", + IPs: "192.168.1.2/24", + }, + }, + SecureBoot: true, + Virtualized: false, + SerialNumber: "NODE-1234-ABCD", + } + + body, _ := json.Marshal(nodeData) + + // Create auth headers + timestamp := time.Now().Unix() + message := fmt.Sprintf("%d:%v", timestamp, twinID) + signature := ed25519.Sign(privateKey, []byte(message)) + + req, _ := http.NewRequest("POST", "http://localhost:8080/v1/nodes", bytes.NewReader(body)) + + // Set required headers + req.Header.Set("X-Auth", fmt.Sprintf( + "%s:%s", + base64.StdEncoding.EncodeToString([]byte(message)), + base64.StdEncoding.EncodeToString(signature), + )) + req.Header.Set("Content-Type", "application/json") + + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + body, _ := io.ReadAll(resp.Body) + return fmt.Errorf("node registration failed: %s", string(body)) + } + var result struct { + NodeID uint64 `json:"node_id"` + } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + fmt.Println("Error: ", err) + return err + } + fmt.Println(result.NodeID) + return nil +} + +func main() { + privateKey, twinID, err := createAccountExample() + if err != nil { + fmt.Println("Error sending request:", err) + return + } + authenticatedRequestExample(twinID, privateKey) + getAccountExample(twinID) + farmID, err := createFarmExample(twinID, privateKey) + if err != nil { + fmt.Println("Error creating farm:", err) + return + } + fmt.Println("farm: ", farmID) + err = registerNodeExample(twinID, farmID, privateKey) + if err != nil { + fmt.Println("Error registering node:", err) + return + } + +} diff --git a/node-registrar/pkg/server/middlewares.go b/node-registrar/pkg/server/middlewares.go index a5150a3..72a5a59 100644 --- a/node-registrar/pkg/server/middlewares.go +++ b/node-registrar/pkg/server/middlewares.go @@ -71,7 +71,6 @@ func (s *Server) AuthMiddleware() gin.HandlerFunc { return } - // Verify signature (supports both ED25519 and SR25519) twinID, err := strconv.ParseUint(twinIDStr, 10, 64) if err != nil { abortWithError(c, http.StatusBadRequest, "Invalid twin ID format") @@ -88,7 +87,6 @@ func (s *Server) AuthMiddleware() gin.HandlerFunc { if err != nil { abortWithError(c, http.StatusBadRequest, fmt.Sprintf("invalid stored public key: %v", err)) return - // Store verified twin ID in context, must be checked form the handlers to ensure altred resources belongs to same user } sig, err := base64.StdEncoding.DecodeString(signatureB64) @@ -97,7 +95,7 @@ func (s *Server) AuthMiddleware() gin.HandlerFunc { return } - // Verify using substrate address and challenge + // Verify signature (supports both ED25519 and SR25519) if err := verifySignature(storedPK, challenge, sig); err != nil { abortWithError(c, http.StatusUnauthorized, fmt.Sprintf("Signature verification failed: %v", err)) return From 949bd6b80b9fafd062d1bb390a04d16e6ccb91ff Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Tue, 28 Jan 2025 16:54:39 +0200 Subject: [PATCH 13/31] "add endpoint to update node" --- node-registrar/pkg/db/nodes.go | 14 ++++++ node-registrar/pkg/server/handlers.go | 64 +++++++++++++++++++++++++++ node-registrar/pkg/server/routes.go | 1 + 3 files changed, 79 insertions(+) diff --git a/node-registrar/pkg/db/nodes.go b/node-registrar/pkg/db/nodes.go index 356f5cc..5cab438 100644 --- a/node-registrar/pkg/db/nodes.go +++ b/node-registrar/pkg/db/nodes.go @@ -53,6 +53,20 @@ func (db *Database) RegisterNode(node Node) (uint64, error) { return node.NodeID, nil } +func (db *Database) UpdateNode(nodeID uint64, updates map[string]interface{}) error { + result := db.gormDB.Model(&Node{}). + Where("node_id = ?", nodeID). + Updates(updates) + + if result.Error != nil { + return result.Error + } + if result.RowsAffected == 0 { + return ErrRecordNotFound + } + return nil +} + // Uptime updates the uptime for a specific node func (db *Database) GetUptimeReports(nodeID uint64, start, end time.Time) ([]UptimeReport, error) { var reports []UptimeReport diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 3471223..1465def 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -329,6 +329,70 @@ func (s Server) registerNodeHandler(c *gin.Context) { }) } +type UpdateNodeRequest struct { + FarmID uint64 `json:"farm_id" binding:"required,min=1"` + Resources db.Resources `json:"resources" binding:"required,min=1"` + Location db.Location `json:"location" binding:"required"` + Interfaces []db.Interface `json:"interfaces" binding:"required,dive"` + SecureBoot bool `json:"secure_boot" binding:"required"` + Virtualized bool `json:"virtualized" binding:"required"` + SerialNumber string `json:"serial_number" binding:"required"` +} + +func (s *Server) updateNodeHandler(c *gin.Context) { + nodeID, err := strconv.ParseUint(c.Param("node_id"), 10, 64) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid node ID"}) + return + } + + existingNode, err := s.db.GetNode(nodeID) + if err != nil { + if errors.Is(err, db.ErrRecordNotFound) { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": "node not found"}) + return + } + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "database error"}) + return + } + + ensureOwner(c, existingNode.TwinID) + if c.IsAborted() { + return + } + + var req UpdateNodeRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid request body"}) + return + } + + // Prepare update fields + updates := map[string]interface{}{ + "farm_id": req.FarmID, + "resources": req.Resources, + "location": req.Location, + "interfaces": req.Interfaces, + "secure_boot": req.SecureBoot, + "virtualized": req.Virtualized, + "serial_number": req.SerialNumber, + } + if req.FarmID != existingNode.FarmID { + updates["approved"] = false + } + + if err := s.db.UpdateNode(nodeID, updates); err != nil { + if errors.Is(err, db.ErrRecordNotFound) { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": "node not found"}) + return + } + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "failed to update node"}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "node updated successfully"}) +} + type UptimeReportRequest struct { Uptime time.Duration `json:"uptime" binding:"required"` Timestamp time.Time `json:"timestamp" binding:"required"` diff --git a/node-registrar/pkg/server/routes.go b/node-registrar/pkg/server/routes.go index f62e8a4..7048ba6 100644 --- a/node-registrar/pkg/server/routes.go +++ b/node-registrar/pkg/server/routes.go @@ -27,6 +27,7 @@ func (s *Server) SetupRoutes() { // protected by node key nodeRoutes.Use(s.AuthMiddleware()) nodeRoutes.POST("/", s.registerNodeHandler) + nodeRoutes.PATCH("/:node_id", s.updateNodeHandler) nodeRoutes.POST("/:node_id/uptime", s.uptimeReportHandler) // Account routes From aa90b47d63784b704fa7f8071a31f5929beeda41 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Tue, 28 Jan 2025 18:04:38 +0200 Subject: [PATCH 14/31] Switch from url param to query param in getAccount route/handler and support query by pubkey --- node-registrar/pkg/db/accounts.go | 21 +++++++++-- node-registrar/pkg/server/handlers.go | 53 ++++++++++++++++++++++----- node-registrar/pkg/server/routes.go | 2 +- 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/node-registrar/pkg/db/accounts.go b/node-registrar/pkg/db/accounts.go index 2642b2f..c78dba7 100644 --- a/node-registrar/pkg/db/accounts.go +++ b/node-registrar/pkg/db/accounts.go @@ -32,13 +32,26 @@ func (db *Database) UpdateAccount(twinID uint64, relays pq.StringArray, rmbEncKe } // GetAccountByTwinID retrieves an account by its twin ID -func (db *Database) GetAccount(twinID uint64) (*Account, error) { +func (db *Database) GetAccount(twinID uint64) (Account, error) { var account Account if err := db.gormDB.First(&account, twinID).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, ErrRecordNotFound + return Account{}, ErrRecordNotFound } - return nil, err + return Account{}, err } - return &account, nil + return account, nil +} + +func (db *Database) GetAccountByPublicKey(publicKey string) (Account, error) { + var account Account + result := db.gormDB.Where("public_key = ?", publicKey).First(&account) + + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return Account{}, ErrRecordNotFound + } + return Account{}, result.Error + } + return account, nil } diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 1465def..81b6597 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -618,23 +618,58 @@ func (s *Server) updateAccountHandler(c *gin.Context) { // @Failure 404 {object} gin.H "Account not found" // @Router /accounts/{twin_id} [get] func (s *Server) getAccountHandler(c *gin.Context) { - twinID, err := strconv.ParseUint(c.Param("twin_id"), 10, 64) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "invalid twin ID"}) + twinIDParam := c.Query("twin_id") + publicKeyParam := c.Query("public_key") + + // Validate only one parameter is provided + if twinIDParam != "" && publicKeyParam != "" { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{ + "error": "provide either twin_id or public_key, not both", + }) return } - account, err := s.db.GetAccount(twinID) - if err != nil { - if err == db.ErrRecordNotFound { - c.JSON(http.StatusNotFound, gin.H{"error": "account not found"}) + if twinIDParam == "" && publicKeyParam == "" { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{ + "error": "must provide either twin_id or public_key parameter", + }) + return + } + + if twinIDParam != "" { + twinID, err := strconv.ParseUint(twinIDParam, 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid twin ID"}) + return + } + + account, err := s.db.GetAccount(twinID) + if err != nil { + if err == db.ErrRecordNotFound { + c.JSON(http.StatusNotFound, gin.H{"error": "account not found"}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get account"}) return } - c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get account"}) + + c.JSON(http.StatusOK, account) return } - c.JSON(http.StatusOK, account) + if publicKeyParam != "" { + account, err := s.db.GetAccountByPublicKey(publicKeyParam) + if err != nil { + if err == db.ErrRecordNotFound { + c.JSON(http.StatusNotFound, gin.H{"error": "account not found"}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get account"}) + return + } + c.JSON(http.StatusOK, account) + return + } } // Helper function to validate public key format diff --git a/node-registrar/pkg/server/routes.go b/node-registrar/pkg/server/routes.go index 7048ba6..58bf63f 100644 --- a/node-registrar/pkg/server/routes.go +++ b/node-registrar/pkg/server/routes.go @@ -33,7 +33,7 @@ func (s *Server) SetupRoutes() { // Account routes accountRoutes := v1.Group("accounts") accountRoutes.POST("/", s.createAccountHandler) - accountRoutes.GET("/:twin_id", s.getAccountHandler) + accountRoutes.GET("/", s.getAccountHandler) // protected by farmer key accountRoutes.Use(s.AuthMiddleware()) accountRoutes.PATCH("/:twin_id", s.updateAccountHandler) From b4a7de964ce58fa6074376badcb3c22245e9a63f Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Tue, 28 Jan 2025 18:40:44 +0200 Subject: [PATCH 15/31] add zos version endpoints to set and get zOS version --- node-registrar/cmds/registrar.go | 14 +++++---- node-registrar/pkg/db/db.go | 1 + node-registrar/pkg/db/models.go | 6 ++++ node-registrar/pkg/db/nodes.go | 25 ++++++++++++++++ node-registrar/pkg/server/handlers.go | 42 +++++++++++++++++++++++++++ node-registrar/pkg/server/routes.go | 8 +++++ node-registrar/pkg/server/server.go | 11 +++---- 7 files changed, 96 insertions(+), 11 deletions(-) diff --git a/node-registrar/cmds/registrar.go b/node-registrar/cmds/registrar.go index 0da4eaf..3459c77 100644 --- a/node-registrar/cmds/registrar.go +++ b/node-registrar/cmds/registrar.go @@ -19,11 +19,12 @@ import ( type flags struct { db.Config - debug bool - version bool - domain string - serverPort uint - network string + debug bool + version bool + domain string + serverPort uint + network string + adminTwinID uint64 } var ( @@ -55,6 +56,7 @@ func Run() error { flag.UintVar(&f.serverPort, "server-port", 8080, "server port") flag.StringVar(&f.domain, "domain", "", "domain on which the server will be served") flag.StringVar(&f.network, "network", "dev", "the registrar network") + flag.Uint64Var(&f.adminTwinID, "admin-twin-id", 0, "admin twin ID") flag.Parse() f.SqlLogLevel = logger.LogLevel(sqlLogLevel) @@ -86,7 +88,7 @@ func Run() error { } }() - s, err := server.NewServer(db, f.network) + s, err := server.NewServer(db, f.network, f.adminTwinID) if err != nil { return errors.Wrap(err, "failed to start gin server") } diff --git a/node-registrar/pkg/db/db.go b/node-registrar/pkg/db/db.go index 3de0a1c..25c8b32 100644 --- a/node-registrar/pkg/db/db.go +++ b/node-registrar/pkg/db/db.go @@ -77,6 +77,7 @@ func (db Database) autoMigrate() error { &Farm{}, &Node{}, &UptimeReport{}, + &ZosVersion{}, ); err != nil { return errors.Wrap(err, "failed to migrate tables") } diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index b6114e2..ff991c0 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -63,6 +63,12 @@ type UptimeReport struct { WasRestart bool // True if this report followed a restart CreatedAt time.Time } + +type ZosVersion struct { + Key string `gorm:"primaryKey;size:50"` + Version string `gorm:"not null"` +} + type Interface struct { Name string `json:"name"` Mac string `json:"mac"` diff --git a/node-registrar/pkg/db/nodes.go b/node-registrar/pkg/db/nodes.go index 5cab438..0d2130e 100644 --- a/node-registrar/pkg/db/nodes.go +++ b/node-registrar/pkg/db/nodes.go @@ -78,3 +78,28 @@ func (db *Database) GetUptimeReports(nodeID uint64, start, end time.Time) ([]Upt func (db *Database) CreateUptimeReport(report *UptimeReport) error { return db.gormDB.Create(report).Error } + +func (db *Database) SetZOSVersion(version string) error { + var current ZosVersion + err := db.gormDB.FirstOrCreate(¤t, ZosVersion{Key: "zos_4"}).Error + if err != nil { + return err + } + + if current.Version == version { + return errors.New("version already set") + } + + return db.gormDB.Model(¤t).Update("version", version).Error +} + +func (db *Database) GetZOSVersion() (string, error) { + var setting ZosVersion + if err := db.gormDB.Where("key = ?", "zos_4").First(&setting).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return "", ErrRecordNotFound + } + return "", err + } + return setting.Version, nil +} diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 81b6597..0240411 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -672,6 +672,48 @@ func (s *Server) getAccountHandler(c *gin.Context) { } } +type ZOSVersionRequest struct { + Version string `json:"version" binding:"required,base64"` +} + +func (s *Server) setZOSVersionHandler(c *gin.Context) { + ensureOwner(c, s.adminTwinID) + if c.IsAborted() { + return + } + + var req ZOSVersionRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if err := s.db.SetZOSVersion(req.Version); err != nil { + status := http.StatusInternalServerError + if err.Error() == "version already set" { + status = http.StatusConflict + } + c.AbortWithStatusJSON(status, gin.H{"error": err.Error()}) + return + } + + c.Status(http.StatusOK) +} + +func (s *Server) getZOSVersionHandler(c *gin.Context) { + version, err := s.db.GetZOSVersion() + if err != nil { + if errors.Is(err, db.ErrRecordNotFound) { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": "zos version not set"}) + return + } + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "database error"}) + return + } + + c.JSON(http.StatusOK, gin.H{"version": version}) +} + // Helper function to validate public key format func isValidPublicKey(publicKeyBase64 string) bool { publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64) diff --git a/node-registrar/pkg/server/routes.go b/node-registrar/pkg/server/routes.go index 58bf63f..fb73b9d 100644 --- a/node-registrar/pkg/server/routes.go +++ b/node-registrar/pkg/server/routes.go @@ -37,4 +37,12 @@ func (s *Server) SetupRoutes() { // protected by farmer key accountRoutes.Use(s.AuthMiddleware()) accountRoutes.PATCH("/:twin_id", s.updateAccountHandler) + + // zOS Version endpoints + zosRoutes := v1.Group("/zos") + zosRoutes.GET("/version", s.getZOSVersionHandler) + // protected by admin key + zosRoutes.Use(s.AuthMiddleware()) + zosRoutes.PUT("/version", s.setZOSVersionHandler) + } diff --git a/node-registrar/pkg/server/server.go b/node-registrar/pkg/server/server.go index 6abd6b8..b9b0a78 100644 --- a/node-registrar/pkg/server/server.go +++ b/node-registrar/pkg/server/server.go @@ -12,15 +12,16 @@ import ( ) type Server struct { - router *gin.Engine - db db.Database - network string + router *gin.Engine + db db.Database + network string + adminTwinID uint64 } -func NewServer(db db.Database, network string) (s Server, err error) { +func NewServer(db db.Database, network string, adminTwinID uint64) (s Server, err error) { router := gin.Default() - s = Server{router, db, network} + s = Server{router, db, network, adminTwinID} s.SetupRoutes() return From 9ce37cb608e0a39b9bee41f3a7b0240d309acf53 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Wed, 29 Jan 2025 12:09:55 +0200 Subject: [PATCH 16/31] update swagger doc for zos endpoints --- node-registrar/pkg/server/handlers.go | 32 ++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 0240411..cef7f47 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -606,17 +606,18 @@ func (s *Server) updateAccountHandler(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "account updated successfully"}) } -// getAccountHandler retrieves an account by twin ID -// @Summary Retrieve an account by twin ID -// @Description This endpoint retrieves an account by its twin ID. +// getAccountHandler retrieves an account by twin ID or public key +// @Summary Retrieve an account by twin ID or public key +// @Description This endpoint retrieves an account by its twin ID or public key. // @Tags accounts // @Accept json // @Produce json -// @Param twin_id path uint64 true "Twin ID of the account" +// @Param twin_id query uint64 false "Twin ID of the account" +// @Param public_key query string false "Base64 decoded Public key of the account" // @Success 200 {object} db.Account "Account details" -// @Failure 400 {object} gin.H "Invalid twin ID" +// @Failure 400 {object} gin.H "Invalid request" // @Failure 404 {object} gin.H "Account not found" -// @Router /accounts/{twin_id} [get] +// @Router /accounts [get] func (s *Server) getAccountHandler(c *gin.Context) { twinIDParam := c.Query("twin_id") publicKeyParam := c.Query("public_key") @@ -676,6 +677,17 @@ type ZOSVersionRequest struct { Version string `json:"version" binding:"required,base64"` } +// @Summary Set ZOS Version +// @Description Sets the ZOS version +// @Tags ZOS +// @Accept json +// @Produce json +// @Param body body ZOSVersionRequest true "Update ZOS Version Request" +// @Success 200 {object} gin.H "OK" +// @Failure 400 {object} gin.H "Bad Request" +// @Failure 409 {object} gin.H "Conflict" +// @Failure 500 {object} gin.H "Internal Server Error" +// @Router /zos/version [post] func (s *Server) setZOSVersionHandler(c *gin.Context) { ensureOwner(c, s.adminTwinID) if c.IsAborted() { @@ -700,6 +712,14 @@ func (s *Server) setZOSVersionHandler(c *gin.Context) { c.Status(http.StatusOK) } +// @Summary Get ZOS Version +// @Description Gets the ZOS version +// @Tags ZOS +// @Produce json +// @Success 200 {object} gin.H "OK" +// @Failure 404 {object} gin.H "Not Found" +// @Failure 500 {object} gin.H "Internal Server Error" +// @Router /zos/version [get] func (s *Server) getZOSVersionHandler(c *gin.Context) { version, err := s.db.GetZOSVersion() if err != nil { From f34249c400221e6744194f2368d57cf705efaaa1 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Wed, 29 Jan 2025 12:41:43 +0200 Subject: [PATCH 17/31] generate swagger docs --- node-registrar/cmds/docs/docs.go | 1005 ++++++++++++++++++++++++- node-registrar/cmds/docs/swagger.json | 1005 ++++++++++++++++++++++++- node-registrar/cmds/docs/swagger.yaml | 679 ++++++++++++++++- node-registrar/go.mod | 13 +- node-registrar/go.sum | 26 +- node-registrar/pkg/db/models.go | 3 +- 6 files changed, 2705 insertions(+), 26 deletions(-) diff --git a/node-registrar/cmds/docs/docs.go b/node-registrar/cmds/docs/docs.go index 6676994..6981391 100644 --- a/node-registrar/cmds/docs/docs.go +++ b/node-registrar/cmds/docs/docs.go @@ -14,7 +14,1010 @@ const docTemplate = `{ }, "host": "{{.Host}}", "basePath": "{{.BasePath}}", - "paths": {} + "paths": { + "/accounts": { + "get": { + "description": "This endpoint retrieves an account by its twin ID or public key.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "accounts" + ], + "summary": "Retrieve an account by twin ID or public key", + "parameters": [ + { + "type": "integer", + "description": "Twin ID of the account", + "name": "twin_id", + "in": "query" + }, + { + "type": "string", + "description": "Base64 decoded Public key of the account", + "name": "public_key", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Account details", + "schema": { + "$ref": "#/definitions/db.Account" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Account not found", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + }, + "post": { + "description": "Create a new twin account with cryptographic verification", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "accounts" + ], + "summary": "Create new account", + "parameters": [ + { + "description": "Account creation data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.AccountCreationRequest" + } + } + ], + "responses": { + "201": { + "description": "Created account details", + "schema": { + "$ref": "#/definitions/db.Account" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "409": { + "description": "Account already exists", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, + "/accounts/{twin_id}": { + "patch": { + "description": "Updates an account's relays and RMB encryption key", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "accounts" + ], + "summary": "Update account details", + "parameters": [ + { + "type": "integer", + "description": "Twin ID of the account", + "name": "twin_id", + "in": "path", + "required": true + }, + { + "description": "Account details to update", + "name": "account", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.UpdateAccountRequest" + } + } + ], + "responses": { + "200": { + "description": "Account updated successfully", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Account not found", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, + "/farms": { + "get": { + "description": "Get a list of farms with optional filters", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "farms" + ], + "summary": "List farms", + "parameters": [ + { + "type": "string", + "description": "Filter by farm name", + "name": "farm_name", + "in": "query" + }, + { + "type": "integer", + "description": "Filter by farm ID", + "name": "farm_id", + "in": "query" + }, + { + "type": "integer", + "description": "Filter by twin ID", + "name": "twin_id", + "in": "query" + }, + { + "type": "integer", + "default": 1, + "description": "Page number", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 10, + "description": "Results per page", + "name": "size", + "in": "query" + } + ], + "responses": { + "200": { + "description": "List of farms", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + }, + "post": { + "description": "Create a new farm entry", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "farms" + ], + "summary": "Create new farm", + "parameters": [ + { + "description": "Farm creation data", + "name": "farm", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/db.Farm" + } + } + ], + "responses": { + "201": { + "description": "Farm created successfully", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "409": { + "description": "Farm already exists", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, + "/farms/{farm_id}": { + "get": { + "description": "Get details for a specific farm", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "farms" + ], + "summary": "Get farm details", + "parameters": [ + { + "type": "integer", + "description": "Farm ID", + "name": "farm_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Farm details", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Invalid farm ID", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Farm not found", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + }, + "patch": { + "description": "Update existing farm details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "farms" + ], + "summary": "Update farm", + "parameters": [ + { + "type": "integer", + "description": "Farm ID", + "name": "farm_id", + "in": "path", + "required": true + }, + { + "description": "Farm update data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.UpdateFarmRequest" + } + } + ], + "responses": { + "200": { + "description": "Farm updated successfully", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Farm not found", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, + "/nodes": { + "get": { + "description": "Get a list of nodes with optional filters", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "nodes" + ], + "summary": "List nodes", + "parameters": [ + { + "type": "integer", + "description": "Filter by node ID", + "name": "node_id", + "in": "query" + }, + { + "type": "integer", + "description": "Filter by farm ID", + "name": "farm_id", + "in": "query" + }, + { + "type": "integer", + "description": "Filter by twin ID", + "name": "twin_id", + "in": "query" + }, + { + "type": "string", + "description": "Filter by status", + "name": "status", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter by health status", + "name": "healthy", + "in": "query" + }, + { + "type": "integer", + "default": 1, + "description": "Page number", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 10, + "description": "Results per page", + "name": "size", + "in": "query" + } + ], + "responses": { + "200": { + "description": "List of nodes", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + }, + "post": { + "description": "Register a new node in the system", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "nodes" + ], + "summary": "Register new node", + "parameters": [ + { + "description": "Node registration data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.NodeRegistrationRequest" + } + } + ], + "responses": { + "201": { + "description": "Node registered successfully", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "409": { + "description": "Node already exists", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, + "/nodes/{node_id}": { + "get": { + "description": "Get details for a specific node", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "nodes" + ], + "summary": "Get node details", + "parameters": [ + { + "type": "integer", + "description": "Node ID", + "name": "node_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Node details", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Invalid node ID", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Node not found", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, + "/nodes/{node_id}/uptime": { + "post": { + "description": "Submit uptime report for a node", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "nodes" + ], + "summary": "Report node uptime", + "parameters": [ + { + "type": "integer", + "description": "Node ID", + "name": "node_id", + "in": "path", + "required": true + }, + { + "description": "Uptime report data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.UptimeReportRequest" + } + } + ], + "responses": { + "201": { + "description": "Uptime reported successfully", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Node not found", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, + "/zos/version": { + "get": { + "description": "Gets the ZOS version", + "produces": [ + "application/json" + ], + "tags": [ + "ZOS" + ], + "summary": "Get ZOS Version", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + }, + "post": { + "description": "Sets the ZOS version", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ZOS" + ], + "summary": "Set ZOS Version", + "parameters": [ + { + "description": "Update ZOS Version Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.ZOSVersionRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + } + }, + "definitions": { + "db.Account": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "farms": { + "description": "Relations | likely we need to use OnDelete:RESTRICT (Prevent Twin deletion if farms exist)\n@swagger:ignore", + "type": "array", + "items": { + "$ref": "#/definitions/db.Farm" + } + }, + "publicKey": { + "description": "The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substrate echo system?\n(still SS58 can be used or plain base58 ,TBD)", + "type": "string" + }, + "relays": { + "description": "Optional list of relay domains", + "type": "array", + "items": { + "type": "string" + } + }, + "rmb_enc_key": { + "description": "Optional base64 encoded public key for rmb communication", + "type": "string" + }, + "twinID": { + "type": "integer" + }, + "updatedAt": { + "type": "string" + } + } + }, + "db.Farm": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "dedicated": { + "type": "boolean" + }, + "farm_id": { + "type": "integer" + }, + "farm_name": { + "type": "string" + }, + "nodes": { + "description": "@swagger:ignore", + "type": "array", + "items": { + "$ref": "#/definitions/db.Node" + } + }, + "twin_id": { + "description": "Farmer account reference", + "type": "integer" + }, + "updatedAt": { + "type": "string" + } + } + }, + "db.Interface": { + "type": "object", + "properties": { + "ips": { + "type": "string" + }, + "mac": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "db.Location": { + "type": "object", + "properties": { + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "latitude": { + "type": "string" + }, + "longitude": { + "type": "string" + } + } + }, + "db.Node": { + "type": "object", + "properties": { + "approved": { + "type": "boolean" + }, + "createdAt": { + "type": "string" + }, + "farm_id": { + "description": "Constraints set to prevents unintended account deletion if linked Farms/nodes exist.", + "type": "integer" + }, + "interface": { + "type": "array", + "items": { + "$ref": "#/definitions/db.Interface" + } + }, + "location": { + "$ref": "#/definitions/db.Location" + }, + "node_id": { + "type": "integer" + }, + "resources": { + "description": "PublicConfig PublicConfig ` + "`" + `json:\"public_config\" gorm:\"type:json\"` + "`" + `", + "allOf": [ + { + "$ref": "#/definitions/db.Resources" + } + ] + }, + "secureBoot": { + "type": "boolean" + }, + "serialNumber": { + "type": "string" + }, + "twin_id": { + "description": "Node account reference", + "type": "integer" + }, + "updatedAt": { + "type": "string" + }, + "uptime": { + "type": "array", + "items": { + "$ref": "#/definitions/db.UptimeReport" + } + }, + "virtualized": { + "type": "boolean" + } + } + }, + "db.Resources": { + "type": "object", + "properties": { + "cru": { + "type": "integer" + }, + "hru": { + "type": "integer" + }, + "mru": { + "type": "integer" + }, + "sru": { + "type": "integer" + } + } + }, + "db.UptimeReport": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "duration": { + "description": "Uptime duration for this period", + "allOf": [ + { + "$ref": "#/definitions/time.Duration" + } + ] + }, + "id": { + "type": "integer" + }, + "nodeID": { + "type": "integer" + }, + "timestamp": { + "type": "string" + }, + "wasRestart": { + "description": "True if this report followed a restart", + "type": "boolean" + } + } + }, + "gin.H": { + "type": "object", + "additionalProperties": {} + }, + "server.AccountCreationRequest": { + "type": "object", + "required": [ + "public_key", + "signature", + "timestamp" + ], + "properties": { + "public_key": { + "description": "base64 encoded", + "type": "string" + }, + "relays": { + "type": "array", + "items": { + "type": "string" + } + }, + "rmb_enc_key": { + "type": "string" + }, + "signature": { + "description": "the registrar expect a signature of a message with format ` + "`" + `timestampStr:publicKeyBase64` + "`" + `\n- signature format: base64(ed25519_or_sr22519_signature)", + "type": "string" + }, + "timestamp": { + "type": "integer" + } + } + }, + "server.NodeRegistrationRequest": { + "type": "object", + "required": [ + "farm_id", + "interfaces", + "location", + "resources", + "serial_number", + "twin_id" + ], + "properties": { + "farm_id": { + "type": "integer", + "minimum": 1 + }, + "interfaces": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/db.Interface" + } + }, + "location": { + "$ref": "#/definitions/db.Location" + }, + "resources": { + "$ref": "#/definitions/db.Resources" + }, + "secure_boot": { + "type": "boolean" + }, + "serial_number": { + "type": "string" + }, + "twin_id": { + "type": "integer", + "minimum": 1 + }, + "virtualized": { + "type": "boolean" + } + } + }, + "server.UpdateAccountRequest": { + "type": "object", + "properties": { + "relays": { + "type": "array", + "items": { + "type": "string" + } + }, + "rmb_enc_key": { + "type": "string" + } + } + }, + "server.UpdateFarmRequest": { + "type": "object", + "required": [ + "farm_name" + ], + "properties": { + "farm_name": { + "type": "string", + "maxLength": 40, + "minLength": 1 + } + } + }, + "server.UptimeReportRequest": { + "type": "object", + "required": [ + "timestamp", + "uptime" + ], + "properties": { + "timestamp": { + "type": "string" + }, + "uptime": { + "$ref": "#/definitions/time.Duration" + } + } + }, + "server.ZOSVersionRequest": { + "type": "object", + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + } + } + }, + "time.Duration": { + "type": "integer", + "enum": [ + -9223372036854775808, + 9223372036854775807, + 1, + 1000, + 1000000, + 1000000000, + 60000000000, + 3600000000000 + ], + "x-enum-varnames": [ + "minDuration", + "maxDuration", + "Nanosecond", + "Microsecond", + "Millisecond", + "Second", + "Minute", + "Hour" + ] + } + } }` // SwaggerInfo holds exported Swagger Info so clients can modify it diff --git a/node-registrar/cmds/docs/swagger.json b/node-registrar/cmds/docs/swagger.json index ec416cd..f5a90ac 100644 --- a/node-registrar/cmds/docs/swagger.json +++ b/node-registrar/cmds/docs/swagger.json @@ -3,5 +3,1008 @@ "info": { "contact": {} }, - "paths": {} + "paths": { + "/accounts": { + "get": { + "description": "This endpoint retrieves an account by its twin ID or public key.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "accounts" + ], + "summary": "Retrieve an account by twin ID or public key", + "parameters": [ + { + "type": "integer", + "description": "Twin ID of the account", + "name": "twin_id", + "in": "query" + }, + { + "type": "string", + "description": "Base64 decoded Public key of the account", + "name": "public_key", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Account details", + "schema": { + "$ref": "#/definitions/db.Account" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Account not found", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + }, + "post": { + "description": "Create a new twin account with cryptographic verification", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "accounts" + ], + "summary": "Create new account", + "parameters": [ + { + "description": "Account creation data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.AccountCreationRequest" + } + } + ], + "responses": { + "201": { + "description": "Created account details", + "schema": { + "$ref": "#/definitions/db.Account" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "409": { + "description": "Account already exists", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, + "/accounts/{twin_id}": { + "patch": { + "description": "Updates an account's relays and RMB encryption key", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "accounts" + ], + "summary": "Update account details", + "parameters": [ + { + "type": "integer", + "description": "Twin ID of the account", + "name": "twin_id", + "in": "path", + "required": true + }, + { + "description": "Account details to update", + "name": "account", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.UpdateAccountRequest" + } + } + ], + "responses": { + "200": { + "description": "Account updated successfully", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Account not found", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, + "/farms": { + "get": { + "description": "Get a list of farms with optional filters", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "farms" + ], + "summary": "List farms", + "parameters": [ + { + "type": "string", + "description": "Filter by farm name", + "name": "farm_name", + "in": "query" + }, + { + "type": "integer", + "description": "Filter by farm ID", + "name": "farm_id", + "in": "query" + }, + { + "type": "integer", + "description": "Filter by twin ID", + "name": "twin_id", + "in": "query" + }, + { + "type": "integer", + "default": 1, + "description": "Page number", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 10, + "description": "Results per page", + "name": "size", + "in": "query" + } + ], + "responses": { + "200": { + "description": "List of farms", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + }, + "post": { + "description": "Create a new farm entry", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "farms" + ], + "summary": "Create new farm", + "parameters": [ + { + "description": "Farm creation data", + "name": "farm", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/db.Farm" + } + } + ], + "responses": { + "201": { + "description": "Farm created successfully", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "409": { + "description": "Farm already exists", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, + "/farms/{farm_id}": { + "get": { + "description": "Get details for a specific farm", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "farms" + ], + "summary": "Get farm details", + "parameters": [ + { + "type": "integer", + "description": "Farm ID", + "name": "farm_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Farm details", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Invalid farm ID", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Farm not found", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + }, + "patch": { + "description": "Update existing farm details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "farms" + ], + "summary": "Update farm", + "parameters": [ + { + "type": "integer", + "description": "Farm ID", + "name": "farm_id", + "in": "path", + "required": true + }, + { + "description": "Farm update data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.UpdateFarmRequest" + } + } + ], + "responses": { + "200": { + "description": "Farm updated successfully", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Farm not found", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, + "/nodes": { + "get": { + "description": "Get a list of nodes with optional filters", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "nodes" + ], + "summary": "List nodes", + "parameters": [ + { + "type": "integer", + "description": "Filter by node ID", + "name": "node_id", + "in": "query" + }, + { + "type": "integer", + "description": "Filter by farm ID", + "name": "farm_id", + "in": "query" + }, + { + "type": "integer", + "description": "Filter by twin ID", + "name": "twin_id", + "in": "query" + }, + { + "type": "string", + "description": "Filter by status", + "name": "status", + "in": "query" + }, + { + "type": "boolean", + "description": "Filter by health status", + "name": "healthy", + "in": "query" + }, + { + "type": "integer", + "default": 1, + "description": "Page number", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 10, + "description": "Results per page", + "name": "size", + "in": "query" + } + ], + "responses": { + "200": { + "description": "List of nodes", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + }, + "post": { + "description": "Register a new node in the system", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "nodes" + ], + "summary": "Register new node", + "parameters": [ + { + "description": "Node registration data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.NodeRegistrationRequest" + } + } + ], + "responses": { + "201": { + "description": "Node registered successfully", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "409": { + "description": "Node already exists", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, + "/nodes/{node_id}": { + "get": { + "description": "Get details for a specific node", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "nodes" + ], + "summary": "Get node details", + "parameters": [ + { + "type": "integer", + "description": "Node ID", + "name": "node_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Node details", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Invalid node ID", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Node not found", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, + "/nodes/{node_id}/uptime": { + "post": { + "description": "Submit uptime report for a node", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "nodes" + ], + "summary": "Report node uptime", + "parameters": [ + { + "type": "integer", + "description": "Node ID", + "name": "node_id", + "in": "path", + "required": true + }, + { + "description": "Uptime report data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.UptimeReportRequest" + } + } + ], + "responses": { + "201": { + "description": "Uptime reported successfully", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Node not found", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, + "/zos/version": { + "get": { + "description": "Gets the ZOS version", + "produces": [ + "application/json" + ], + "tags": [ + "ZOS" + ], + "summary": "Get ZOS Version", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + }, + "post": { + "description": "Sets the ZOS version", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ZOS" + ], + "summary": "Set ZOS Version", + "parameters": [ + { + "description": "Update ZOS Version Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.ZOSVersionRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + } + }, + "definitions": { + "db.Account": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "farms": { + "description": "Relations | likely we need to use OnDelete:RESTRICT (Prevent Twin deletion if farms exist)\n@swagger:ignore", + "type": "array", + "items": { + "$ref": "#/definitions/db.Farm" + } + }, + "publicKey": { + "description": "The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substrate echo system?\n(still SS58 can be used or plain base58 ,TBD)", + "type": "string" + }, + "relays": { + "description": "Optional list of relay domains", + "type": "array", + "items": { + "type": "string" + } + }, + "rmb_enc_key": { + "description": "Optional base64 encoded public key for rmb communication", + "type": "string" + }, + "twinID": { + "type": "integer" + }, + "updatedAt": { + "type": "string" + } + } + }, + "db.Farm": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "dedicated": { + "type": "boolean" + }, + "farm_id": { + "type": "integer" + }, + "farm_name": { + "type": "string" + }, + "nodes": { + "description": "@swagger:ignore", + "type": "array", + "items": { + "$ref": "#/definitions/db.Node" + } + }, + "twin_id": { + "description": "Farmer account reference", + "type": "integer" + }, + "updatedAt": { + "type": "string" + } + } + }, + "db.Interface": { + "type": "object", + "properties": { + "ips": { + "type": "string" + }, + "mac": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "db.Location": { + "type": "object", + "properties": { + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "latitude": { + "type": "string" + }, + "longitude": { + "type": "string" + } + } + }, + "db.Node": { + "type": "object", + "properties": { + "approved": { + "type": "boolean" + }, + "createdAt": { + "type": "string" + }, + "farm_id": { + "description": "Constraints set to prevents unintended account deletion if linked Farms/nodes exist.", + "type": "integer" + }, + "interface": { + "type": "array", + "items": { + "$ref": "#/definitions/db.Interface" + } + }, + "location": { + "$ref": "#/definitions/db.Location" + }, + "node_id": { + "type": "integer" + }, + "resources": { + "description": "PublicConfig PublicConfig `json:\"public_config\" gorm:\"type:json\"`", + "allOf": [ + { + "$ref": "#/definitions/db.Resources" + } + ] + }, + "secureBoot": { + "type": "boolean" + }, + "serialNumber": { + "type": "string" + }, + "twin_id": { + "description": "Node account reference", + "type": "integer" + }, + "updatedAt": { + "type": "string" + }, + "uptime": { + "type": "array", + "items": { + "$ref": "#/definitions/db.UptimeReport" + } + }, + "virtualized": { + "type": "boolean" + } + } + }, + "db.Resources": { + "type": "object", + "properties": { + "cru": { + "type": "integer" + }, + "hru": { + "type": "integer" + }, + "mru": { + "type": "integer" + }, + "sru": { + "type": "integer" + } + } + }, + "db.UptimeReport": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "duration": { + "description": "Uptime duration for this period", + "allOf": [ + { + "$ref": "#/definitions/time.Duration" + } + ] + }, + "id": { + "type": "integer" + }, + "nodeID": { + "type": "integer" + }, + "timestamp": { + "type": "string" + }, + "wasRestart": { + "description": "True if this report followed a restart", + "type": "boolean" + } + } + }, + "gin.H": { + "type": "object", + "additionalProperties": {} + }, + "server.AccountCreationRequest": { + "type": "object", + "required": [ + "public_key", + "signature", + "timestamp" + ], + "properties": { + "public_key": { + "description": "base64 encoded", + "type": "string" + }, + "relays": { + "type": "array", + "items": { + "type": "string" + } + }, + "rmb_enc_key": { + "type": "string" + }, + "signature": { + "description": "the registrar expect a signature of a message with format `timestampStr:publicKeyBase64`\n- signature format: base64(ed25519_or_sr22519_signature)", + "type": "string" + }, + "timestamp": { + "type": "integer" + } + } + }, + "server.NodeRegistrationRequest": { + "type": "object", + "required": [ + "farm_id", + "interfaces", + "location", + "resources", + "serial_number", + "twin_id" + ], + "properties": { + "farm_id": { + "type": "integer", + "minimum": 1 + }, + "interfaces": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/db.Interface" + } + }, + "location": { + "$ref": "#/definitions/db.Location" + }, + "resources": { + "$ref": "#/definitions/db.Resources" + }, + "secure_boot": { + "type": "boolean" + }, + "serial_number": { + "type": "string" + }, + "twin_id": { + "type": "integer", + "minimum": 1 + }, + "virtualized": { + "type": "boolean" + } + } + }, + "server.UpdateAccountRequest": { + "type": "object", + "properties": { + "relays": { + "type": "array", + "items": { + "type": "string" + } + }, + "rmb_enc_key": { + "type": "string" + } + } + }, + "server.UpdateFarmRequest": { + "type": "object", + "required": [ + "farm_name" + ], + "properties": { + "farm_name": { + "type": "string", + "maxLength": 40, + "minLength": 1 + } + } + }, + "server.UptimeReportRequest": { + "type": "object", + "required": [ + "timestamp", + "uptime" + ], + "properties": { + "timestamp": { + "type": "string" + }, + "uptime": { + "$ref": "#/definitions/time.Duration" + } + } + }, + "server.ZOSVersionRequest": { + "type": "object", + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + } + } + }, + "time.Duration": { + "type": "integer", + "enum": [ + -9223372036854775808, + 9223372036854775807, + 1, + 1000, + 1000000, + 1000000000, + 60000000000, + 3600000000000 + ], + "x-enum-varnames": [ + "minDuration", + "maxDuration", + "Nanosecond", + "Microsecond", + "Millisecond", + "Second", + "Minute", + "Hour" + ] + } + } } \ No newline at end of file diff --git a/node-registrar/cmds/docs/swagger.yaml b/node-registrar/cmds/docs/swagger.yaml index b64379c..7f03c59 100644 --- a/node-registrar/cmds/docs/swagger.yaml +++ b/node-registrar/cmds/docs/swagger.yaml @@ -1,4 +1,681 @@ +definitions: + db.Account: + properties: + createdAt: + type: string + farms: + description: |- + Relations | likely we need to use OnDelete:RESTRICT (Prevent Twin deletion if farms exist) + @swagger:ignore + items: + $ref: '#/definitions/db.Farm' + type: array + publicKey: + description: |- + The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substrate echo system? + (still SS58 can be used or plain base58 ,TBD) + type: string + relays: + description: Optional list of relay domains + items: + type: string + type: array + rmb_enc_key: + description: Optional base64 encoded public key for rmb communication + type: string + twinID: + type: integer + updatedAt: + type: string + type: object + db.Farm: + properties: + createdAt: + type: string + dedicated: + type: boolean + farm_id: + type: integer + farm_name: + type: string + nodes: + description: '@swagger:ignore' + items: + $ref: '#/definitions/db.Node' + type: array + twin_id: + description: Farmer account reference + type: integer + updatedAt: + type: string + type: object + db.Interface: + properties: + ips: + type: string + mac: + type: string + name: + type: string + type: object + db.Location: + properties: + city: + type: string + country: + type: string + latitude: + type: string + longitude: + type: string + type: object + db.Node: + properties: + approved: + type: boolean + createdAt: + type: string + farm_id: + description: Constraints set to prevents unintended account deletion if linked + Farms/nodes exist. + type: integer + interface: + items: + $ref: '#/definitions/db.Interface' + type: array + location: + $ref: '#/definitions/db.Location' + node_id: + type: integer + resources: + allOf: + - $ref: '#/definitions/db.Resources' + description: PublicConfig PublicConfig `json:"public_config" gorm:"type:json"` + secureBoot: + type: boolean + serialNumber: + type: string + twin_id: + description: Node account reference + type: integer + updatedAt: + type: string + uptime: + items: + $ref: '#/definitions/db.UptimeReport' + type: array + virtualized: + type: boolean + type: object + db.Resources: + properties: + cru: + type: integer + hru: + type: integer + mru: + type: integer + sru: + type: integer + type: object + db.UptimeReport: + properties: + createdAt: + type: string + duration: + allOf: + - $ref: '#/definitions/time.Duration' + description: Uptime duration for this period + id: + type: integer + nodeID: + type: integer + timestamp: + type: string + wasRestart: + description: True if this report followed a restart + type: boolean + type: object + gin.H: + additionalProperties: {} + type: object + server.AccountCreationRequest: + properties: + public_key: + description: base64 encoded + type: string + relays: + items: + type: string + type: array + rmb_enc_key: + type: string + signature: + description: |- + the registrar expect a signature of a message with format `timestampStr:publicKeyBase64` + - signature format: base64(ed25519_or_sr22519_signature) + type: string + timestamp: + type: integer + required: + - public_key + - signature + - timestamp + type: object + server.NodeRegistrationRequest: + properties: + farm_id: + minimum: 1 + type: integer + interfaces: + items: + $ref: '#/definitions/db.Interface' + minItems: 1 + type: array + location: + $ref: '#/definitions/db.Location' + resources: + $ref: '#/definitions/db.Resources' + secure_boot: + type: boolean + serial_number: + type: string + twin_id: + minimum: 1 + type: integer + virtualized: + type: boolean + required: + - farm_id + - interfaces + - location + - resources + - serial_number + - twin_id + type: object + server.UpdateAccountRequest: + properties: + relays: + items: + type: string + type: array + rmb_enc_key: + type: string + type: object + server.UpdateFarmRequest: + properties: + farm_name: + maxLength: 40 + minLength: 1 + type: string + required: + - farm_name + type: object + server.UptimeReportRequest: + properties: + timestamp: + type: string + uptime: + $ref: '#/definitions/time.Duration' + required: + - timestamp + - uptime + type: object + server.ZOSVersionRequest: + properties: + version: + type: string + required: + - version + type: object + time.Duration: + enum: + - -9223372036854775808 + - 9223372036854775807 + - 1 + - 1000 + - 1000000 + - 1000000000 + - 60000000000 + - 3600000000000 + type: integer + x-enum-varnames: + - minDuration + - maxDuration + - Nanosecond + - Microsecond + - Millisecond + - Second + - Minute + - Hour info: contact: {} -paths: {} +paths: + /accounts: + get: + consumes: + - application/json + description: This endpoint retrieves an account by its twin ID or public key. + parameters: + - description: Twin ID of the account + in: query + name: twin_id + type: integer + - description: Base64 decoded Public key of the account + in: query + name: public_key + type: string + produces: + - application/json + responses: + "200": + description: Account details + schema: + $ref: '#/definitions/db.Account' + "400": + description: Invalid request + schema: + $ref: '#/definitions/gin.H' + "404": + description: Account not found + schema: + $ref: '#/definitions/gin.H' + summary: Retrieve an account by twin ID or public key + tags: + - accounts + post: + consumes: + - application/json + description: Create a new twin account with cryptographic verification + parameters: + - description: Account creation data + in: body + name: request + required: true + schema: + $ref: '#/definitions/server.AccountCreationRequest' + produces: + - application/json + responses: + "201": + description: Created account details + schema: + $ref: '#/definitions/db.Account' + "400": + description: Invalid request + schema: + $ref: '#/definitions/gin.H' + "409": + description: Account already exists + schema: + $ref: '#/definitions/gin.H' + summary: Create new account + tags: + - accounts + /accounts/{twin_id}: + patch: + consumes: + - application/json + description: Updates an account's relays and RMB encryption key + parameters: + - description: Twin ID of the account + in: path + name: twin_id + required: true + type: integer + - description: Account details to update + in: body + name: account + required: true + schema: + $ref: '#/definitions/server.UpdateAccountRequest' + produces: + - application/json + responses: + "200": + description: Account updated successfully + schema: + $ref: '#/definitions/gin.H' + "400": + description: Invalid request + schema: + $ref: '#/definitions/gin.H' + "404": + description: Account not found + schema: + $ref: '#/definitions/gin.H' + summary: Update account details + tags: + - accounts + /farms: + get: + consumes: + - application/json + description: Get a list of farms with optional filters + parameters: + - description: Filter by farm name + in: query + name: farm_name + type: string + - description: Filter by farm ID + in: query + name: farm_id + type: integer + - description: Filter by twin ID + in: query + name: twin_id + type: integer + - default: 1 + description: Page number + in: query + name: page + type: integer + - default: 10 + description: Results per page + in: query + name: size + type: integer + produces: + - application/json + responses: + "200": + description: List of farms + schema: + $ref: '#/definitions/gin.H' + "400": + description: Bad request + schema: + $ref: '#/definitions/gin.H' + summary: List farms + tags: + - farms + post: + consumes: + - application/json + description: Create a new farm entry + parameters: + - description: Farm creation data + in: body + name: farm + required: true + schema: + $ref: '#/definitions/db.Farm' + produces: + - application/json + responses: + "201": + description: Farm created successfully + schema: + $ref: '#/definitions/gin.H' + "400": + description: Invalid request + schema: + $ref: '#/definitions/gin.H' + "409": + description: Farm already exists + schema: + $ref: '#/definitions/gin.H' + summary: Create new farm + tags: + - farms + /farms/{farm_id}: + get: + consumes: + - application/json + description: Get details for a specific farm + parameters: + - description: Farm ID + in: path + name: farm_id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: Farm details + schema: + $ref: '#/definitions/gin.H' + "400": + description: Invalid farm ID + schema: + $ref: '#/definitions/gin.H' + "404": + description: Farm not found + schema: + $ref: '#/definitions/gin.H' + summary: Get farm details + tags: + - farms + patch: + consumes: + - application/json + description: Update existing farm details + parameters: + - description: Farm ID + in: path + name: farm_id + required: true + type: integer + - description: Farm update data + in: body + name: request + required: true + schema: + $ref: '#/definitions/server.UpdateFarmRequest' + produces: + - application/json + responses: + "200": + description: Farm updated successfully + schema: + $ref: '#/definitions/gin.H' + "400": + description: Invalid request + schema: + $ref: '#/definitions/gin.H' + "404": + description: Farm not found + schema: + $ref: '#/definitions/gin.H' + summary: Update farm + tags: + - farms + /nodes: + get: + consumes: + - application/json + description: Get a list of nodes with optional filters + parameters: + - description: Filter by node ID + in: query + name: node_id + type: integer + - description: Filter by farm ID + in: query + name: farm_id + type: integer + - description: Filter by twin ID + in: query + name: twin_id + type: integer + - description: Filter by status + in: query + name: status + type: string + - description: Filter by health status + in: query + name: healthy + type: boolean + - default: 1 + description: Page number + in: query + name: page + type: integer + - default: 10 + description: Results per page + in: query + name: size + type: integer + produces: + - application/json + responses: + "200": + description: List of nodes + schema: + $ref: '#/definitions/gin.H' + "400": + description: Bad request + schema: + $ref: '#/definitions/gin.H' + summary: List nodes + tags: + - nodes + post: + consumes: + - application/json + description: Register a new node in the system + parameters: + - description: Node registration data + in: body + name: request + required: true + schema: + $ref: '#/definitions/server.NodeRegistrationRequest' + produces: + - application/json + responses: + "201": + description: Node registered successfully + schema: + $ref: '#/definitions/gin.H' + "400": + description: Invalid request + schema: + $ref: '#/definitions/gin.H' + "409": + description: Node already exists + schema: + $ref: '#/definitions/gin.H' + summary: Register new node + tags: + - nodes + /nodes/{node_id}: + get: + consumes: + - application/json + description: Get details for a specific node + parameters: + - description: Node ID + in: path + name: node_id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: Node details + schema: + $ref: '#/definitions/gin.H' + "400": + description: Invalid node ID + schema: + $ref: '#/definitions/gin.H' + "404": + description: Node not found + schema: + $ref: '#/definitions/gin.H' + summary: Get node details + tags: + - nodes + /nodes/{node_id}/uptime: + post: + consumes: + - application/json + description: Submit uptime report for a node + parameters: + - description: Node ID + in: path + name: node_id + required: true + type: integer + - description: Uptime report data + in: body + name: request + required: true + schema: + $ref: '#/definitions/server.UptimeReportRequest' + produces: + - application/json + responses: + "201": + description: Uptime reported successfully + schema: + $ref: '#/definitions/gin.H' + "400": + description: Invalid request + schema: + $ref: '#/definitions/gin.H' + "404": + description: Node not found + schema: + $ref: '#/definitions/gin.H' + summary: Report node uptime + tags: + - nodes + /zos/version: + get: + description: Gets the ZOS version + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/gin.H' + "404": + description: Not Found + schema: + $ref: '#/definitions/gin.H' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/gin.H' + summary: Get ZOS Version + tags: + - ZOS + post: + consumes: + - application/json + description: Sets the ZOS version + parameters: + - description: Update ZOS Version Request + in: body + name: body + required: true + schema: + $ref: '#/definitions/server.ZOSVersionRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/gin.H' + "400": + description: Bad Request + schema: + $ref: '#/definitions/gin.H' + "409": + description: Conflict + schema: + $ref: '#/definitions/gin.H' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/gin.H' + summary: Set ZOS Version + tags: + - ZOS swagger: "2.0" diff --git a/node-registrar/go.mod b/node-registrar/go.mod index 3ba5c67..5d83def 100644 --- a/node-registrar/go.mod +++ b/node-registrar/go.mod @@ -20,8 +20,6 @@ require ( require ( github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/bytedance/sonic v1.11.6 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/cloudwego/base64x v0.1.4 // indirect @@ -31,10 +29,10 @@ require ( github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.6 // indirect - github.com/go-openapi/spec v0.20.4 // indirect - github.com/go-openapi/swag v0.19.15 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/spec v0.20.9 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect @@ -51,7 +49,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/mailru/easyjson v0.7.6 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect @@ -69,6 +67,5 @@ require ( golang.org/x/text v0.16.0 // indirect golang.org/x/tools v0.22.0 // indirect google.golang.org/protobuf v1.34.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/node-registrar/go.sum b/node-registrar/go.sum index 8e163d0..d3758e8 100644 --- a/node-registrar/go.sum +++ b/node-registrar/go.sum @@ -2,10 +2,6 @@ github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRr github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= @@ -41,15 +37,18 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= -github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8= +github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -90,6 +89,7 @@ github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuV github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -102,8 +102,9 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -172,7 +173,6 @@ golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= @@ -184,7 +184,6 @@ golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -199,7 +198,6 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index ff991c0..163211d 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -19,6 +19,7 @@ type Account struct { // (still SS58 can be used or plain base58 ,TBD) PublicKey string `gorm:"type:text;not null;unique"` // Relations | likely we need to use OnDelete:RESTRICT (Prevent Twin deletion if farms exist) + // @swagger:ignore Farms []Farm `gorm:"foreignKey:TwinID;references:TwinID;constraint:OnDelete:RESTRICT"` } @@ -29,7 +30,7 @@ type Farm struct { Dedicated bool `json:"dedicated"` CreatedAt time.Time UpdatedAt time.Time - + // @swagger:ignore Nodes []Node `gorm:"foreignKey:FarmID;references:FarmID;constraint:OnDelete:RESTRICT" json:"nodes"` } From 2cac04b0f86ffe479096d7e9864fa960a9f0198d Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Wed, 29 Jan 2025 13:32:08 +0200 Subject: [PATCH 18/31] update docs --- node-registrar/README.md | 133 +++++++++++++++++++++++++++++---------- 1 file changed, 101 insertions(+), 32 deletions(-) diff --git a/node-registrar/README.md b/node-registrar/README.md index 19a55a5..c1df4c5 100644 --- a/node-registrar/README.md +++ b/node-registrar/README.md @@ -1,19 +1,43 @@ -## Project Documentation +# Node Registrar Service -### Overview -This project provides an API for registring zos nodes using the Go Gin framework and PostgreSQL database. +## Overview + +This project provides an API for registring zos nodes using the Go Gin framework and PostgreSQL database. The API supports operations like registring, listing, and updating farms and nodes, as well as reporting uptime and consumption data for nodes. -### Endpoint Descriptions +## Features + +- **Farm Management** + - Create/update farms with owner authorization + - List farms with filtering/pagination + - Automatic twin ID association via authentication + +- **Node Registration** + - Create/update nodes with owner authorization + - Uptime reporting + - Node metadata management (location, interfaces, specs) + +- **Account System** + - ED25519/SR25519 authentication + - Relay management for RMB communication + +- **Security** + - Challenge-response authentication middleware + - Ownership verification for mutations + - Timestamp replay protection + +## Endpoint Descriptions + +### Farms Endpoints -#### Farms Endpoints 1. **GET /farms/** - List all farms, or use FarmFilter to list specific set of farms. 2. **GET /farms/:farm_id** - Get a specific farm by ID. 3. **POST /farms/** - Create a new farm. 4. **PATCH /farms/** - Update an existing farm. -#### Nodes Endpoints +### Nodes Endpoints + 1. **GET /nodes/** - List all nodes, or use NodeFilter to list specific set of nodes. 2. **GET /nodes/:node_id** - Get a specific node by ID. 3. **POST /nodes/** - Register a new node. @@ -23,52 +47,97 @@ The API supports operations like registring, listing, and updating farms and nod ## Setup Instructions 1. **Start PostgreSQL:** + ```bash make postgres ``` + 2. **Run the Server:** + ```bash make run ``` + 3. **Stop PostgreSQL:** + ```bash make stop-postgres ``` -### Swagger Documentation +## Swagger Documentation + Once the server is running, Swagger documentation can be accessed at: -``` + +```bash http://:/swagger/index.html ``` + Replace `` and `` with the appropriate values. -### How to Use the Server +## How to Use the Server + 1. Use a tool like Postman or cURL to interact with the API. 2. Refer to the Swagger documentation for detailed information about request parameters and response structures. -### How to run the server with docker -1. use the docker file to build the docker image -``` -docker build -t registrar:latest . -``` +## How to run the server with docker + +1. use the docker file to build the docker image + + ```bash + docker build -t registrar:latest . + ``` + 2. run the image + + ```bash + docker run -d \ + -p 8080:8080 \ + --name registrar \ + registrar:latest \ + ./server + --postgres-host= \ + --postgres-port=5432 \ + --postgres-db= \ + --postgres-user= \ + --postgres-password= \ + --ssl-mode=disable \ + --sql-log-level=2 \ + --max-open-conn=10 \ + --max-idle-conn=5 \ + --server-port=8080 \ + -- \ + --network=main\ + --admin_twin_id=1 + --debug + ``` + +## Authentication + +Requests requiring authorization must include: + +```http +X-Auth: Base64(Challenge):Base64(Signature) ``` -docker run -d \ - -p 8080:8080 \ - --name registrar \ - registrar:latest \ - ./server - --postgres-host= \ - --postgres-port=5432 \ - --postgres-db= \ - --postgres-user= \ - --postgres-password= \ - --ssl-mode=disable \ - --sql-log-level=2 \ - --max-open-conn=10 \ - --max-idle-conn=5 \ - --server-port=8080 \ - -- \ - --network=main\ - --debug + +**Challenge Format:** +`:` + +**Signature:** +ED25519/SR25519 signature of challenge bytes + +## Database Schema + +Key Tables: + +- `accounts` - Authentication credentials and relay configs +- `farms` - Farm metadata with owner relationship +- `nodes` - Node hardware/resources specification +- `uptime_reports` - Historical node availability data + +## Development + +### Generating Swagger Docs + +```bash +swag init -g pkg/server/handlers.go --output docs/ ``` From 38de41162052d6bb92ec55b6f7265420a9ea5860 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Wed, 29 Jan 2025 14:00:03 +0200 Subject: [PATCH 19/31] fix swagger docs path --- node-registrar/README.md | 2 +- node-registrar/cmds/docs/docs.go | 1039 ------------------------- node-registrar/cmds/docs/swagger.json | 1010 ------------------------ node-registrar/cmds/docs/swagger.yaml | 681 ---------------- node-registrar/docs/docs.go | 933 ++++++++++++++++------ node-registrar/docs/swagger.json | 933 ++++++++++++++++------ node-registrar/docs/swagger.yaml | 719 ++++++++++++----- 7 files changed, 1891 insertions(+), 3426 deletions(-) delete mode 100644 node-registrar/cmds/docs/docs.go delete mode 100644 node-registrar/cmds/docs/swagger.json delete mode 100644 node-registrar/cmds/docs/swagger.yaml diff --git a/node-registrar/README.md b/node-registrar/README.md index c1df4c5..3820a90 100644 --- a/node-registrar/README.md +++ b/node-registrar/README.md @@ -139,5 +139,5 @@ Key Tables: ### Generating Swagger Docs ```bash -swag init -g pkg/server/handlers.go --output docs/ +swag init -g pkg/server/handlers.go --output docs --parseDependency --parseDepth 2 ``` diff --git a/node-registrar/cmds/docs/docs.go b/node-registrar/cmds/docs/docs.go deleted file mode 100644 index 6981391..0000000 --- a/node-registrar/cmds/docs/docs.go +++ /dev/null @@ -1,1039 +0,0 @@ -// Package docs Code generated by swaggo/swag. DO NOT EDIT -package docs - -import "github.com/swaggo/swag" - -const docTemplate = `{ - "schemes": {{ marshal .Schemes }}, - "swagger": "2.0", - "info": { - "description": "{{escape .Description}}", - "title": "{{.Title}}", - "contact": {}, - "version": "{{.Version}}" - }, - "host": "{{.Host}}", - "basePath": "{{.BasePath}}", - "paths": { - "/accounts": { - "get": { - "description": "This endpoint retrieves an account by its twin ID or public key.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "accounts" - ], - "summary": "Retrieve an account by twin ID or public key", - "parameters": [ - { - "type": "integer", - "description": "Twin ID of the account", - "name": "twin_id", - "in": "query" - }, - { - "type": "string", - "description": "Base64 decoded Public key of the account", - "name": "public_key", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Account details", - "schema": { - "$ref": "#/definitions/db.Account" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "404": { - "description": "Account not found", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - }, - "post": { - "description": "Create a new twin account with cryptographic verification", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "accounts" - ], - "summary": "Create new account", - "parameters": [ - { - "description": "Account creation data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/server.AccountCreationRequest" - } - } - ], - "responses": { - "201": { - "description": "Created account details", - "schema": { - "$ref": "#/definitions/db.Account" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "409": { - "description": "Account already exists", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - } - }, - "/accounts/{twin_id}": { - "patch": { - "description": "Updates an account's relays and RMB encryption key", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "accounts" - ], - "summary": "Update account details", - "parameters": [ - { - "type": "integer", - "description": "Twin ID of the account", - "name": "twin_id", - "in": "path", - "required": true - }, - { - "description": "Account details to update", - "name": "account", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/server.UpdateAccountRequest" - } - } - ], - "responses": { - "200": { - "description": "Account updated successfully", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "404": { - "description": "Account not found", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - } - }, - "/farms": { - "get": { - "description": "Get a list of farms with optional filters", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "farms" - ], - "summary": "List farms", - "parameters": [ - { - "type": "string", - "description": "Filter by farm name", - "name": "farm_name", - "in": "query" - }, - { - "type": "integer", - "description": "Filter by farm ID", - "name": "farm_id", - "in": "query" - }, - { - "type": "integer", - "description": "Filter by twin ID", - "name": "twin_id", - "in": "query" - }, - { - "type": "integer", - "default": 1, - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "default": 10, - "description": "Results per page", - "name": "size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "List of farms", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - }, - "post": { - "description": "Create a new farm entry", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "farms" - ], - "summary": "Create new farm", - "parameters": [ - { - "description": "Farm creation data", - "name": "farm", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/db.Farm" - } - } - ], - "responses": { - "201": { - "description": "Farm created successfully", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "409": { - "description": "Farm already exists", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - } - }, - "/farms/{farm_id}": { - "get": { - "description": "Get details for a specific farm", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "farms" - ], - "summary": "Get farm details", - "parameters": [ - { - "type": "integer", - "description": "Farm ID", - "name": "farm_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Farm details", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Invalid farm ID", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "404": { - "description": "Farm not found", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - }, - "patch": { - "description": "Update existing farm details", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "farms" - ], - "summary": "Update farm", - "parameters": [ - { - "type": "integer", - "description": "Farm ID", - "name": "farm_id", - "in": "path", - "required": true - }, - { - "description": "Farm update data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/server.UpdateFarmRequest" - } - } - ], - "responses": { - "200": { - "description": "Farm updated successfully", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "404": { - "description": "Farm not found", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - } - }, - "/nodes": { - "get": { - "description": "Get a list of nodes with optional filters", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "nodes" - ], - "summary": "List nodes", - "parameters": [ - { - "type": "integer", - "description": "Filter by node ID", - "name": "node_id", - "in": "query" - }, - { - "type": "integer", - "description": "Filter by farm ID", - "name": "farm_id", - "in": "query" - }, - { - "type": "integer", - "description": "Filter by twin ID", - "name": "twin_id", - "in": "query" - }, - { - "type": "string", - "description": "Filter by status", - "name": "status", - "in": "query" - }, - { - "type": "boolean", - "description": "Filter by health status", - "name": "healthy", - "in": "query" - }, - { - "type": "integer", - "default": 1, - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "default": 10, - "description": "Results per page", - "name": "size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "List of nodes", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - }, - "post": { - "description": "Register a new node in the system", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "nodes" - ], - "summary": "Register new node", - "parameters": [ - { - "description": "Node registration data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/server.NodeRegistrationRequest" - } - } - ], - "responses": { - "201": { - "description": "Node registered successfully", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "409": { - "description": "Node already exists", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - } - }, - "/nodes/{node_id}": { - "get": { - "description": "Get details for a specific node", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "nodes" - ], - "summary": "Get node details", - "parameters": [ - { - "type": "integer", - "description": "Node ID", - "name": "node_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Node details", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Invalid node ID", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "404": { - "description": "Node not found", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - } - }, - "/nodes/{node_id}/uptime": { - "post": { - "description": "Submit uptime report for a node", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "nodes" - ], - "summary": "Report node uptime", - "parameters": [ - { - "type": "integer", - "description": "Node ID", - "name": "node_id", - "in": "path", - "required": true - }, - { - "description": "Uptime report data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/server.UptimeReportRequest" - } - } - ], - "responses": { - "201": { - "description": "Uptime reported successfully", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "404": { - "description": "Node not found", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - } - }, - "/zos/version": { - "get": { - "description": "Gets the ZOS version", - "produces": [ - "application/json" - ], - "tags": [ - "ZOS" - ], - "summary": "Get ZOS Version", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - }, - "post": { - "description": "Sets the ZOS version", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ZOS" - ], - "summary": "Set ZOS Version", - "parameters": [ - { - "description": "Update ZOS Version Request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/server.ZOSVersionRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "409": { - "description": "Conflict", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - } - } - }, - "definitions": { - "db.Account": { - "type": "object", - "properties": { - "createdAt": { - "type": "string" - }, - "farms": { - "description": "Relations | likely we need to use OnDelete:RESTRICT (Prevent Twin deletion if farms exist)\n@swagger:ignore", - "type": "array", - "items": { - "$ref": "#/definitions/db.Farm" - } - }, - "publicKey": { - "description": "The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substrate echo system?\n(still SS58 can be used or plain base58 ,TBD)", - "type": "string" - }, - "relays": { - "description": "Optional list of relay domains", - "type": "array", - "items": { - "type": "string" - } - }, - "rmb_enc_key": { - "description": "Optional base64 encoded public key for rmb communication", - "type": "string" - }, - "twinID": { - "type": "integer" - }, - "updatedAt": { - "type": "string" - } - } - }, - "db.Farm": { - "type": "object", - "properties": { - "createdAt": { - "type": "string" - }, - "dedicated": { - "type": "boolean" - }, - "farm_id": { - "type": "integer" - }, - "farm_name": { - "type": "string" - }, - "nodes": { - "description": "@swagger:ignore", - "type": "array", - "items": { - "$ref": "#/definitions/db.Node" - } - }, - "twin_id": { - "description": "Farmer account reference", - "type": "integer" - }, - "updatedAt": { - "type": "string" - } - } - }, - "db.Interface": { - "type": "object", - "properties": { - "ips": { - "type": "string" - }, - "mac": { - "type": "string" - }, - "name": { - "type": "string" - } - } - }, - "db.Location": { - "type": "object", - "properties": { - "city": { - "type": "string" - }, - "country": { - "type": "string" - }, - "latitude": { - "type": "string" - }, - "longitude": { - "type": "string" - } - } - }, - "db.Node": { - "type": "object", - "properties": { - "approved": { - "type": "boolean" - }, - "createdAt": { - "type": "string" - }, - "farm_id": { - "description": "Constraints set to prevents unintended account deletion if linked Farms/nodes exist.", - "type": "integer" - }, - "interface": { - "type": "array", - "items": { - "$ref": "#/definitions/db.Interface" - } - }, - "location": { - "$ref": "#/definitions/db.Location" - }, - "node_id": { - "type": "integer" - }, - "resources": { - "description": "PublicConfig PublicConfig ` + "`" + `json:\"public_config\" gorm:\"type:json\"` + "`" + `", - "allOf": [ - { - "$ref": "#/definitions/db.Resources" - } - ] - }, - "secureBoot": { - "type": "boolean" - }, - "serialNumber": { - "type": "string" - }, - "twin_id": { - "description": "Node account reference", - "type": "integer" - }, - "updatedAt": { - "type": "string" - }, - "uptime": { - "type": "array", - "items": { - "$ref": "#/definitions/db.UptimeReport" - } - }, - "virtualized": { - "type": "boolean" - } - } - }, - "db.Resources": { - "type": "object", - "properties": { - "cru": { - "type": "integer" - }, - "hru": { - "type": "integer" - }, - "mru": { - "type": "integer" - }, - "sru": { - "type": "integer" - } - } - }, - "db.UptimeReport": { - "type": "object", - "properties": { - "createdAt": { - "type": "string" - }, - "duration": { - "description": "Uptime duration for this period", - "allOf": [ - { - "$ref": "#/definitions/time.Duration" - } - ] - }, - "id": { - "type": "integer" - }, - "nodeID": { - "type": "integer" - }, - "timestamp": { - "type": "string" - }, - "wasRestart": { - "description": "True if this report followed a restart", - "type": "boolean" - } - } - }, - "gin.H": { - "type": "object", - "additionalProperties": {} - }, - "server.AccountCreationRequest": { - "type": "object", - "required": [ - "public_key", - "signature", - "timestamp" - ], - "properties": { - "public_key": { - "description": "base64 encoded", - "type": "string" - }, - "relays": { - "type": "array", - "items": { - "type": "string" - } - }, - "rmb_enc_key": { - "type": "string" - }, - "signature": { - "description": "the registrar expect a signature of a message with format ` + "`" + `timestampStr:publicKeyBase64` + "`" + `\n- signature format: base64(ed25519_or_sr22519_signature)", - "type": "string" - }, - "timestamp": { - "type": "integer" - } - } - }, - "server.NodeRegistrationRequest": { - "type": "object", - "required": [ - "farm_id", - "interfaces", - "location", - "resources", - "serial_number", - "twin_id" - ], - "properties": { - "farm_id": { - "type": "integer", - "minimum": 1 - }, - "interfaces": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/definitions/db.Interface" - } - }, - "location": { - "$ref": "#/definitions/db.Location" - }, - "resources": { - "$ref": "#/definitions/db.Resources" - }, - "secure_boot": { - "type": "boolean" - }, - "serial_number": { - "type": "string" - }, - "twin_id": { - "type": "integer", - "minimum": 1 - }, - "virtualized": { - "type": "boolean" - } - } - }, - "server.UpdateAccountRequest": { - "type": "object", - "properties": { - "relays": { - "type": "array", - "items": { - "type": "string" - } - }, - "rmb_enc_key": { - "type": "string" - } - } - }, - "server.UpdateFarmRequest": { - "type": "object", - "required": [ - "farm_name" - ], - "properties": { - "farm_name": { - "type": "string", - "maxLength": 40, - "minLength": 1 - } - } - }, - "server.UptimeReportRequest": { - "type": "object", - "required": [ - "timestamp", - "uptime" - ], - "properties": { - "timestamp": { - "type": "string" - }, - "uptime": { - "$ref": "#/definitions/time.Duration" - } - } - }, - "server.ZOSVersionRequest": { - "type": "object", - "required": [ - "version" - ], - "properties": { - "version": { - "type": "string" - } - } - }, - "time.Duration": { - "type": "integer", - "enum": [ - -9223372036854775808, - 9223372036854775807, - 1, - 1000, - 1000000, - 1000000000, - 60000000000, - 3600000000000 - ], - "x-enum-varnames": [ - "minDuration", - "maxDuration", - "Nanosecond", - "Microsecond", - "Millisecond", - "Second", - "Minute", - "Hour" - ] - } - } -}` - -// SwaggerInfo holds exported Swagger Info so clients can modify it -var SwaggerInfo = &swag.Spec{ - Version: "", - Host: "", - BasePath: "", - Schemes: []string{}, - Title: "", - Description: "", - InfoInstanceName: "swagger", - SwaggerTemplate: docTemplate, - LeftDelim: "{{", - RightDelim: "}}", -} - -func init() { - swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) -} diff --git a/node-registrar/cmds/docs/swagger.json b/node-registrar/cmds/docs/swagger.json deleted file mode 100644 index f5a90ac..0000000 --- a/node-registrar/cmds/docs/swagger.json +++ /dev/null @@ -1,1010 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "contact": {} - }, - "paths": { - "/accounts": { - "get": { - "description": "This endpoint retrieves an account by its twin ID or public key.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "accounts" - ], - "summary": "Retrieve an account by twin ID or public key", - "parameters": [ - { - "type": "integer", - "description": "Twin ID of the account", - "name": "twin_id", - "in": "query" - }, - { - "type": "string", - "description": "Base64 decoded Public key of the account", - "name": "public_key", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Account details", - "schema": { - "$ref": "#/definitions/db.Account" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "404": { - "description": "Account not found", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - }, - "post": { - "description": "Create a new twin account with cryptographic verification", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "accounts" - ], - "summary": "Create new account", - "parameters": [ - { - "description": "Account creation data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/server.AccountCreationRequest" - } - } - ], - "responses": { - "201": { - "description": "Created account details", - "schema": { - "$ref": "#/definitions/db.Account" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "409": { - "description": "Account already exists", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - } - }, - "/accounts/{twin_id}": { - "patch": { - "description": "Updates an account's relays and RMB encryption key", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "accounts" - ], - "summary": "Update account details", - "parameters": [ - { - "type": "integer", - "description": "Twin ID of the account", - "name": "twin_id", - "in": "path", - "required": true - }, - { - "description": "Account details to update", - "name": "account", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/server.UpdateAccountRequest" - } - } - ], - "responses": { - "200": { - "description": "Account updated successfully", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "404": { - "description": "Account not found", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - } - }, - "/farms": { - "get": { - "description": "Get a list of farms with optional filters", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "farms" - ], - "summary": "List farms", - "parameters": [ - { - "type": "string", - "description": "Filter by farm name", - "name": "farm_name", - "in": "query" - }, - { - "type": "integer", - "description": "Filter by farm ID", - "name": "farm_id", - "in": "query" - }, - { - "type": "integer", - "description": "Filter by twin ID", - "name": "twin_id", - "in": "query" - }, - { - "type": "integer", - "default": 1, - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "default": 10, - "description": "Results per page", - "name": "size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "List of farms", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - }, - "post": { - "description": "Create a new farm entry", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "farms" - ], - "summary": "Create new farm", - "parameters": [ - { - "description": "Farm creation data", - "name": "farm", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/db.Farm" - } - } - ], - "responses": { - "201": { - "description": "Farm created successfully", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "409": { - "description": "Farm already exists", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - } - }, - "/farms/{farm_id}": { - "get": { - "description": "Get details for a specific farm", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "farms" - ], - "summary": "Get farm details", - "parameters": [ - { - "type": "integer", - "description": "Farm ID", - "name": "farm_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Farm details", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Invalid farm ID", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "404": { - "description": "Farm not found", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - }, - "patch": { - "description": "Update existing farm details", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "farms" - ], - "summary": "Update farm", - "parameters": [ - { - "type": "integer", - "description": "Farm ID", - "name": "farm_id", - "in": "path", - "required": true - }, - { - "description": "Farm update data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/server.UpdateFarmRequest" - } - } - ], - "responses": { - "200": { - "description": "Farm updated successfully", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "404": { - "description": "Farm not found", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - } - }, - "/nodes": { - "get": { - "description": "Get a list of nodes with optional filters", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "nodes" - ], - "summary": "List nodes", - "parameters": [ - { - "type": "integer", - "description": "Filter by node ID", - "name": "node_id", - "in": "query" - }, - { - "type": "integer", - "description": "Filter by farm ID", - "name": "farm_id", - "in": "query" - }, - { - "type": "integer", - "description": "Filter by twin ID", - "name": "twin_id", - "in": "query" - }, - { - "type": "string", - "description": "Filter by status", - "name": "status", - "in": "query" - }, - { - "type": "boolean", - "description": "Filter by health status", - "name": "healthy", - "in": "query" - }, - { - "type": "integer", - "default": 1, - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "default": 10, - "description": "Results per page", - "name": "size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "List of nodes", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - }, - "post": { - "description": "Register a new node in the system", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "nodes" - ], - "summary": "Register new node", - "parameters": [ - { - "description": "Node registration data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/server.NodeRegistrationRequest" - } - } - ], - "responses": { - "201": { - "description": "Node registered successfully", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "409": { - "description": "Node already exists", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - } - }, - "/nodes/{node_id}": { - "get": { - "description": "Get details for a specific node", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "nodes" - ], - "summary": "Get node details", - "parameters": [ - { - "type": "integer", - "description": "Node ID", - "name": "node_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Node details", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Invalid node ID", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "404": { - "description": "Node not found", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - } - }, - "/nodes/{node_id}/uptime": { - "post": { - "description": "Submit uptime report for a node", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "nodes" - ], - "summary": "Report node uptime", - "parameters": [ - { - "type": "integer", - "description": "Node ID", - "name": "node_id", - "in": "path", - "required": true - }, - { - "description": "Uptime report data", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/server.UptimeReportRequest" - } - } - ], - "responses": { - "201": { - "description": "Uptime reported successfully", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "404": { - "description": "Node not found", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - } - }, - "/zos/version": { - "get": { - "description": "Gets the ZOS version", - "produces": [ - "application/json" - ], - "tags": [ - "ZOS" - ], - "summary": "Get ZOS Version", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - }, - "post": { - "description": "Sets the ZOS version", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ZOS" - ], - "summary": "Set ZOS Version", - "parameters": [ - { - "description": "Update ZOS Version Request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/server.ZOSVersionRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "409": { - "description": "Conflict", - "schema": { - "$ref": "#/definitions/gin.H" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/gin.H" - } - } - } - } - } - }, - "definitions": { - "db.Account": { - "type": "object", - "properties": { - "createdAt": { - "type": "string" - }, - "farms": { - "description": "Relations | likely we need to use OnDelete:RESTRICT (Prevent Twin deletion if farms exist)\n@swagger:ignore", - "type": "array", - "items": { - "$ref": "#/definitions/db.Farm" - } - }, - "publicKey": { - "description": "The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substrate echo system?\n(still SS58 can be used or plain base58 ,TBD)", - "type": "string" - }, - "relays": { - "description": "Optional list of relay domains", - "type": "array", - "items": { - "type": "string" - } - }, - "rmb_enc_key": { - "description": "Optional base64 encoded public key for rmb communication", - "type": "string" - }, - "twinID": { - "type": "integer" - }, - "updatedAt": { - "type": "string" - } - } - }, - "db.Farm": { - "type": "object", - "properties": { - "createdAt": { - "type": "string" - }, - "dedicated": { - "type": "boolean" - }, - "farm_id": { - "type": "integer" - }, - "farm_name": { - "type": "string" - }, - "nodes": { - "description": "@swagger:ignore", - "type": "array", - "items": { - "$ref": "#/definitions/db.Node" - } - }, - "twin_id": { - "description": "Farmer account reference", - "type": "integer" - }, - "updatedAt": { - "type": "string" - } - } - }, - "db.Interface": { - "type": "object", - "properties": { - "ips": { - "type": "string" - }, - "mac": { - "type": "string" - }, - "name": { - "type": "string" - } - } - }, - "db.Location": { - "type": "object", - "properties": { - "city": { - "type": "string" - }, - "country": { - "type": "string" - }, - "latitude": { - "type": "string" - }, - "longitude": { - "type": "string" - } - } - }, - "db.Node": { - "type": "object", - "properties": { - "approved": { - "type": "boolean" - }, - "createdAt": { - "type": "string" - }, - "farm_id": { - "description": "Constraints set to prevents unintended account deletion if linked Farms/nodes exist.", - "type": "integer" - }, - "interface": { - "type": "array", - "items": { - "$ref": "#/definitions/db.Interface" - } - }, - "location": { - "$ref": "#/definitions/db.Location" - }, - "node_id": { - "type": "integer" - }, - "resources": { - "description": "PublicConfig PublicConfig `json:\"public_config\" gorm:\"type:json\"`", - "allOf": [ - { - "$ref": "#/definitions/db.Resources" - } - ] - }, - "secureBoot": { - "type": "boolean" - }, - "serialNumber": { - "type": "string" - }, - "twin_id": { - "description": "Node account reference", - "type": "integer" - }, - "updatedAt": { - "type": "string" - }, - "uptime": { - "type": "array", - "items": { - "$ref": "#/definitions/db.UptimeReport" - } - }, - "virtualized": { - "type": "boolean" - } - } - }, - "db.Resources": { - "type": "object", - "properties": { - "cru": { - "type": "integer" - }, - "hru": { - "type": "integer" - }, - "mru": { - "type": "integer" - }, - "sru": { - "type": "integer" - } - } - }, - "db.UptimeReport": { - "type": "object", - "properties": { - "createdAt": { - "type": "string" - }, - "duration": { - "description": "Uptime duration for this period", - "allOf": [ - { - "$ref": "#/definitions/time.Duration" - } - ] - }, - "id": { - "type": "integer" - }, - "nodeID": { - "type": "integer" - }, - "timestamp": { - "type": "string" - }, - "wasRestart": { - "description": "True if this report followed a restart", - "type": "boolean" - } - } - }, - "gin.H": { - "type": "object", - "additionalProperties": {} - }, - "server.AccountCreationRequest": { - "type": "object", - "required": [ - "public_key", - "signature", - "timestamp" - ], - "properties": { - "public_key": { - "description": "base64 encoded", - "type": "string" - }, - "relays": { - "type": "array", - "items": { - "type": "string" - } - }, - "rmb_enc_key": { - "type": "string" - }, - "signature": { - "description": "the registrar expect a signature of a message with format `timestampStr:publicKeyBase64`\n- signature format: base64(ed25519_or_sr22519_signature)", - "type": "string" - }, - "timestamp": { - "type": "integer" - } - } - }, - "server.NodeRegistrationRequest": { - "type": "object", - "required": [ - "farm_id", - "interfaces", - "location", - "resources", - "serial_number", - "twin_id" - ], - "properties": { - "farm_id": { - "type": "integer", - "minimum": 1 - }, - "interfaces": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/definitions/db.Interface" - } - }, - "location": { - "$ref": "#/definitions/db.Location" - }, - "resources": { - "$ref": "#/definitions/db.Resources" - }, - "secure_boot": { - "type": "boolean" - }, - "serial_number": { - "type": "string" - }, - "twin_id": { - "type": "integer", - "minimum": 1 - }, - "virtualized": { - "type": "boolean" - } - } - }, - "server.UpdateAccountRequest": { - "type": "object", - "properties": { - "relays": { - "type": "array", - "items": { - "type": "string" - } - }, - "rmb_enc_key": { - "type": "string" - } - } - }, - "server.UpdateFarmRequest": { - "type": "object", - "required": [ - "farm_name" - ], - "properties": { - "farm_name": { - "type": "string", - "maxLength": 40, - "minLength": 1 - } - } - }, - "server.UptimeReportRequest": { - "type": "object", - "required": [ - "timestamp", - "uptime" - ], - "properties": { - "timestamp": { - "type": "string" - }, - "uptime": { - "$ref": "#/definitions/time.Duration" - } - } - }, - "server.ZOSVersionRequest": { - "type": "object", - "required": [ - "version" - ], - "properties": { - "version": { - "type": "string" - } - } - }, - "time.Duration": { - "type": "integer", - "enum": [ - -9223372036854775808, - 9223372036854775807, - 1, - 1000, - 1000000, - 1000000000, - 60000000000, - 3600000000000 - ], - "x-enum-varnames": [ - "minDuration", - "maxDuration", - "Nanosecond", - "Microsecond", - "Millisecond", - "Second", - "Minute", - "Hour" - ] - } - } -} \ No newline at end of file diff --git a/node-registrar/cmds/docs/swagger.yaml b/node-registrar/cmds/docs/swagger.yaml deleted file mode 100644 index 7f03c59..0000000 --- a/node-registrar/cmds/docs/swagger.yaml +++ /dev/null @@ -1,681 +0,0 @@ -definitions: - db.Account: - properties: - createdAt: - type: string - farms: - description: |- - Relations | likely we need to use OnDelete:RESTRICT (Prevent Twin deletion if farms exist) - @swagger:ignore - items: - $ref: '#/definitions/db.Farm' - type: array - publicKey: - description: |- - The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substrate echo system? - (still SS58 can be used or plain base58 ,TBD) - type: string - relays: - description: Optional list of relay domains - items: - type: string - type: array - rmb_enc_key: - description: Optional base64 encoded public key for rmb communication - type: string - twinID: - type: integer - updatedAt: - type: string - type: object - db.Farm: - properties: - createdAt: - type: string - dedicated: - type: boolean - farm_id: - type: integer - farm_name: - type: string - nodes: - description: '@swagger:ignore' - items: - $ref: '#/definitions/db.Node' - type: array - twin_id: - description: Farmer account reference - type: integer - updatedAt: - type: string - type: object - db.Interface: - properties: - ips: - type: string - mac: - type: string - name: - type: string - type: object - db.Location: - properties: - city: - type: string - country: - type: string - latitude: - type: string - longitude: - type: string - type: object - db.Node: - properties: - approved: - type: boolean - createdAt: - type: string - farm_id: - description: Constraints set to prevents unintended account deletion if linked - Farms/nodes exist. - type: integer - interface: - items: - $ref: '#/definitions/db.Interface' - type: array - location: - $ref: '#/definitions/db.Location' - node_id: - type: integer - resources: - allOf: - - $ref: '#/definitions/db.Resources' - description: PublicConfig PublicConfig `json:"public_config" gorm:"type:json"` - secureBoot: - type: boolean - serialNumber: - type: string - twin_id: - description: Node account reference - type: integer - updatedAt: - type: string - uptime: - items: - $ref: '#/definitions/db.UptimeReport' - type: array - virtualized: - type: boolean - type: object - db.Resources: - properties: - cru: - type: integer - hru: - type: integer - mru: - type: integer - sru: - type: integer - type: object - db.UptimeReport: - properties: - createdAt: - type: string - duration: - allOf: - - $ref: '#/definitions/time.Duration' - description: Uptime duration for this period - id: - type: integer - nodeID: - type: integer - timestamp: - type: string - wasRestart: - description: True if this report followed a restart - type: boolean - type: object - gin.H: - additionalProperties: {} - type: object - server.AccountCreationRequest: - properties: - public_key: - description: base64 encoded - type: string - relays: - items: - type: string - type: array - rmb_enc_key: - type: string - signature: - description: |- - the registrar expect a signature of a message with format `timestampStr:publicKeyBase64` - - signature format: base64(ed25519_or_sr22519_signature) - type: string - timestamp: - type: integer - required: - - public_key - - signature - - timestamp - type: object - server.NodeRegistrationRequest: - properties: - farm_id: - minimum: 1 - type: integer - interfaces: - items: - $ref: '#/definitions/db.Interface' - minItems: 1 - type: array - location: - $ref: '#/definitions/db.Location' - resources: - $ref: '#/definitions/db.Resources' - secure_boot: - type: boolean - serial_number: - type: string - twin_id: - minimum: 1 - type: integer - virtualized: - type: boolean - required: - - farm_id - - interfaces - - location - - resources - - serial_number - - twin_id - type: object - server.UpdateAccountRequest: - properties: - relays: - items: - type: string - type: array - rmb_enc_key: - type: string - type: object - server.UpdateFarmRequest: - properties: - farm_name: - maxLength: 40 - minLength: 1 - type: string - required: - - farm_name - type: object - server.UptimeReportRequest: - properties: - timestamp: - type: string - uptime: - $ref: '#/definitions/time.Duration' - required: - - timestamp - - uptime - type: object - server.ZOSVersionRequest: - properties: - version: - type: string - required: - - version - type: object - time.Duration: - enum: - - -9223372036854775808 - - 9223372036854775807 - - 1 - - 1000 - - 1000000 - - 1000000000 - - 60000000000 - - 3600000000000 - type: integer - x-enum-varnames: - - minDuration - - maxDuration - - Nanosecond - - Microsecond - - Millisecond - - Second - - Minute - - Hour -info: - contact: {} -paths: - /accounts: - get: - consumes: - - application/json - description: This endpoint retrieves an account by its twin ID or public key. - parameters: - - description: Twin ID of the account - in: query - name: twin_id - type: integer - - description: Base64 decoded Public key of the account - in: query - name: public_key - type: string - produces: - - application/json - responses: - "200": - description: Account details - schema: - $ref: '#/definitions/db.Account' - "400": - description: Invalid request - schema: - $ref: '#/definitions/gin.H' - "404": - description: Account not found - schema: - $ref: '#/definitions/gin.H' - summary: Retrieve an account by twin ID or public key - tags: - - accounts - post: - consumes: - - application/json - description: Create a new twin account with cryptographic verification - parameters: - - description: Account creation data - in: body - name: request - required: true - schema: - $ref: '#/definitions/server.AccountCreationRequest' - produces: - - application/json - responses: - "201": - description: Created account details - schema: - $ref: '#/definitions/db.Account' - "400": - description: Invalid request - schema: - $ref: '#/definitions/gin.H' - "409": - description: Account already exists - schema: - $ref: '#/definitions/gin.H' - summary: Create new account - tags: - - accounts - /accounts/{twin_id}: - patch: - consumes: - - application/json - description: Updates an account's relays and RMB encryption key - parameters: - - description: Twin ID of the account - in: path - name: twin_id - required: true - type: integer - - description: Account details to update - in: body - name: account - required: true - schema: - $ref: '#/definitions/server.UpdateAccountRequest' - produces: - - application/json - responses: - "200": - description: Account updated successfully - schema: - $ref: '#/definitions/gin.H' - "400": - description: Invalid request - schema: - $ref: '#/definitions/gin.H' - "404": - description: Account not found - schema: - $ref: '#/definitions/gin.H' - summary: Update account details - tags: - - accounts - /farms: - get: - consumes: - - application/json - description: Get a list of farms with optional filters - parameters: - - description: Filter by farm name - in: query - name: farm_name - type: string - - description: Filter by farm ID - in: query - name: farm_id - type: integer - - description: Filter by twin ID - in: query - name: twin_id - type: integer - - default: 1 - description: Page number - in: query - name: page - type: integer - - default: 10 - description: Results per page - in: query - name: size - type: integer - produces: - - application/json - responses: - "200": - description: List of farms - schema: - $ref: '#/definitions/gin.H' - "400": - description: Bad request - schema: - $ref: '#/definitions/gin.H' - summary: List farms - tags: - - farms - post: - consumes: - - application/json - description: Create a new farm entry - parameters: - - description: Farm creation data - in: body - name: farm - required: true - schema: - $ref: '#/definitions/db.Farm' - produces: - - application/json - responses: - "201": - description: Farm created successfully - schema: - $ref: '#/definitions/gin.H' - "400": - description: Invalid request - schema: - $ref: '#/definitions/gin.H' - "409": - description: Farm already exists - schema: - $ref: '#/definitions/gin.H' - summary: Create new farm - tags: - - farms - /farms/{farm_id}: - get: - consumes: - - application/json - description: Get details for a specific farm - parameters: - - description: Farm ID - in: path - name: farm_id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: Farm details - schema: - $ref: '#/definitions/gin.H' - "400": - description: Invalid farm ID - schema: - $ref: '#/definitions/gin.H' - "404": - description: Farm not found - schema: - $ref: '#/definitions/gin.H' - summary: Get farm details - tags: - - farms - patch: - consumes: - - application/json - description: Update existing farm details - parameters: - - description: Farm ID - in: path - name: farm_id - required: true - type: integer - - description: Farm update data - in: body - name: request - required: true - schema: - $ref: '#/definitions/server.UpdateFarmRequest' - produces: - - application/json - responses: - "200": - description: Farm updated successfully - schema: - $ref: '#/definitions/gin.H' - "400": - description: Invalid request - schema: - $ref: '#/definitions/gin.H' - "404": - description: Farm not found - schema: - $ref: '#/definitions/gin.H' - summary: Update farm - tags: - - farms - /nodes: - get: - consumes: - - application/json - description: Get a list of nodes with optional filters - parameters: - - description: Filter by node ID - in: query - name: node_id - type: integer - - description: Filter by farm ID - in: query - name: farm_id - type: integer - - description: Filter by twin ID - in: query - name: twin_id - type: integer - - description: Filter by status - in: query - name: status - type: string - - description: Filter by health status - in: query - name: healthy - type: boolean - - default: 1 - description: Page number - in: query - name: page - type: integer - - default: 10 - description: Results per page - in: query - name: size - type: integer - produces: - - application/json - responses: - "200": - description: List of nodes - schema: - $ref: '#/definitions/gin.H' - "400": - description: Bad request - schema: - $ref: '#/definitions/gin.H' - summary: List nodes - tags: - - nodes - post: - consumes: - - application/json - description: Register a new node in the system - parameters: - - description: Node registration data - in: body - name: request - required: true - schema: - $ref: '#/definitions/server.NodeRegistrationRequest' - produces: - - application/json - responses: - "201": - description: Node registered successfully - schema: - $ref: '#/definitions/gin.H' - "400": - description: Invalid request - schema: - $ref: '#/definitions/gin.H' - "409": - description: Node already exists - schema: - $ref: '#/definitions/gin.H' - summary: Register new node - tags: - - nodes - /nodes/{node_id}: - get: - consumes: - - application/json - description: Get details for a specific node - parameters: - - description: Node ID - in: path - name: node_id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: Node details - schema: - $ref: '#/definitions/gin.H' - "400": - description: Invalid node ID - schema: - $ref: '#/definitions/gin.H' - "404": - description: Node not found - schema: - $ref: '#/definitions/gin.H' - summary: Get node details - tags: - - nodes - /nodes/{node_id}/uptime: - post: - consumes: - - application/json - description: Submit uptime report for a node - parameters: - - description: Node ID - in: path - name: node_id - required: true - type: integer - - description: Uptime report data - in: body - name: request - required: true - schema: - $ref: '#/definitions/server.UptimeReportRequest' - produces: - - application/json - responses: - "201": - description: Uptime reported successfully - schema: - $ref: '#/definitions/gin.H' - "400": - description: Invalid request - schema: - $ref: '#/definitions/gin.H' - "404": - description: Node not found - schema: - $ref: '#/definitions/gin.H' - summary: Report node uptime - tags: - - nodes - /zos/version: - get: - description: Gets the ZOS version - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/gin.H' - "404": - description: Not Found - schema: - $ref: '#/definitions/gin.H' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/gin.H' - summary: Get ZOS Version - tags: - - ZOS - post: - consumes: - - application/json - description: Sets the ZOS version - parameters: - - description: Update ZOS Version Request - in: body - name: body - required: true - schema: - $ref: '#/definitions/server.ZOSVersionRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/gin.H' - "400": - description: Bad Request - schema: - $ref: '#/definitions/gin.H' - "409": - description: Conflict - schema: - $ref: '#/definitions/gin.H' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/gin.H' - summary: Set ZOS Version - tags: - - ZOS -swagger: "2.0" diff --git a/node-registrar/docs/docs.go b/node-registrar/docs/docs.go index 79bbf70..0b137f7 100644 --- a/node-registrar/docs/docs.go +++ b/node-registrar/docs/docs.go @@ -15,484 +15,621 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { - "/farm/{farm_id}": { + "/accounts": { "get": { - "description": "get a farm with specific id", + "description": "This endpoint retrieves an account by its twin ID or public key.", "consumes": [ "application/json" ], "produces": [ "application/json" ], - "summary": "get farm", + "tags": [ + "accounts" + ], + "summary": "Retrieve an account by twin ID or public key", "parameters": [ { "type": "integer", - "description": "farm id", - "name": "farm_id", + "description": "Twin ID of the account", + "name": "twin_id", + "in": "query" + }, + { + "type": "string", + "description": "Base64 decoded Public key of the account", + "name": "public_key", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Account details", + "schema": { + "$ref": "#/definitions/db.Account" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Account not found", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + }, + "post": { + "description": "Create a new twin account with cryptographic verification", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "accounts" + ], + "summary": "Create new account", + "parameters": [ + { + "description": "Account creation data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.AccountCreationRequest" + } + } + ], + "responses": { + "201": { + "description": "Created account details", + "schema": { + "$ref": "#/definitions/db.Account" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "409": { + "description": "Account already exists", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, + "/accounts/{twin_id}": { + "patch": { + "description": "Updates an account's relays and RMB encryption key", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "accounts" + ], + "summary": "Update account details", + "parameters": [ + { + "type": "integer", + "description": "Twin ID of the account", + "name": "twin_id", "in": "path", "required": true + }, + { + "description": "Account details to update", + "name": "account", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.UpdateAccountRequest" + } } ], "responses": { "200": { - "description": "OK", + "description": "Account updated successfully", "schema": { - "$ref": "#/definitions/db.Farm" + "$ref": "#/definitions/gin.H" } }, "400": { - "description": "Bad Request", - "schema": {} + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Account not found", + "schema": { + "$ref": "#/definitions/gin.H" + } } } } }, - "/farms/": { + "/farms": { "get": { - "description": "list farms with specific filter", + "description": "Get a list of farms with optional filters", "consumes": [ "application/json" ], "produces": [ "application/json" ], - "summary": "list farms", + "tags": [ + "farms" + ], + "summary": "List farms", "parameters": [ { "type": "string", - "description": "farm name", + "description": "Filter by farm name", "name": "farm_name", "in": "query" }, { "type": "integer", - "description": "farm id", + "description": "Filter by farm ID", "name": "farm_id", "in": "query" }, { "type": "integer", - "description": "twin id", + "description": "Filter by twin ID", "name": "twin_id", "in": "query" }, { "type": "integer", + "default": 1, "description": "Page number", "name": "page", "in": "query" }, { "type": "integer", - "description": "Max result per page", + "default": 10, + "description": "Results per page", "name": "size", "in": "query" } ], "responses": { "200": { - "description": "OK", + "description": "List of farms", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/db.Farm" - } + "$ref": "#/definitions/gin.H" } }, "400": { - "description": "Bad Request", - "schema": {} + "description": "Bad request", + "schema": { + "$ref": "#/definitions/gin.H" + } } } }, "post": { - "description": "creates a farm", + "description": "Create a new farm entry", "consumes": [ "application/json" ], "produces": [ "application/json" ], - "summary": "create a farm", + "tags": [ + "farms" + ], + "summary": "Create new farm", "parameters": [ { - "description": "farm id", - "name": "farm_id", + "description": "Farm creation data", + "name": "farm", "in": "body", "required": true, "schema": { - "type": "integer" + "$ref": "#/definitions/db.Farm" } - }, - { - "description": "farm name", - "name": "farm_name", - "in": "body", - "required": true, + } + ], + "responses": { + "201": { + "description": "Farm created successfully", "schema": { - "type": "integer" + "$ref": "#/definitions/gin.H" } }, - { - "description": "twin id", - "name": "twin_id", - "in": "body", - "required": true, + "400": { + "description": "Invalid request", "schema": { - "type": "integer" + "$ref": "#/definitions/gin.H" } }, - { - "description": "dedicated farm", - "name": "dedicated", - "in": "body", + "409": { + "description": "Farm already exists", "schema": { - "type": "boolean" + "$ref": "#/definitions/gin.H" } - }, + } + } + } + }, + "/farms/{farm_id}": { + "get": { + "description": "Get details for a specific farm", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "farms" + ], + "summary": "Get farm details", + "parameters": [ { - "description": "farm free ips", - "name": "farm_free_ips", - "in": "body", - "schema": { - "type": "integer" - } + "type": "integer", + "description": "Farm ID", + "name": "farm_id", + "in": "path", + "required": true } ], "responses": { "200": { - "description": "OK", + "description": "Farm details", "schema": { - "$ref": "#/definitions/db.Farm" + "$ref": "#/definitions/gin.H" } }, "400": { - "description": "Bad Request", - "schema": {} + "description": "Invalid farm ID", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Farm not found", + "schema": { + "$ref": "#/definitions/gin.H" + } } } }, "patch": { - "description": "update a farm", + "description": "Update existing farm details", "consumes": [ "application/json" ], "produces": [ "application/json" ], - "summary": "update a farm", + "tags": [ + "farms" + ], + "summary": "Update farm", "parameters": [ { - "description": "farm id", + "type": "integer", + "description": "Farm ID", "name": "farm_id", - "in": "body", - "required": true, - "schema": { - "type": "integer" - } - }, - { - "description": "farm name", - "name": "farm_name", - "in": "body", - "schema": { - "type": "integer" - } - }, - { - "description": "twin id", - "name": "twin_id", - "in": "body", - "schema": { - "type": "integer" - } - }, - { - "description": "dedicated farm", - "name": "dedicated", - "in": "body", - "schema": { - "type": "boolean" - } + "in": "path", + "required": true }, { - "description": "farm free ips", - "name": "farm_free_ips", + "description": "Farm update data", + "name": "request", "in": "body", + "required": true, "schema": { - "type": "integer" + "$ref": "#/definitions/server.UpdateFarmRequest" } } ], "responses": { "200": { - "description": "OK", + "description": "Farm updated successfully", "schema": { - "$ref": "#/definitions/db.Farm" + "$ref": "#/definitions/gin.H" } }, "400": { - "description": "Bad Request", - "schema": {} + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Farm not found", + "schema": { + "$ref": "#/definitions/gin.H" + } } } } }, - "/nodes/": { + "/nodes": { "get": { - "description": "list nodes with specific filter", + "description": "Get a list of nodes with optional filters", "consumes": [ "application/json" ], "produces": [ "application/json" ], - "summary": "list nodes", + "tags": [ + "nodes" + ], + "summary": "List nodes", "parameters": [ { "type": "integer", - "description": "node id", + "description": "Filter by node ID", "name": "node_id", "in": "query" }, { "type": "integer", - "description": "farm id", + "description": "Filter by farm ID", "name": "farm_id", "in": "query" }, { "type": "integer", - "description": "twin id", + "description": "Filter by twin ID", "name": "twin_id", "in": "query" }, { "type": "string", - "description": "node status", + "description": "Filter by status", "name": "status", "in": "query" }, { "type": "boolean", - "description": "is node healthy", + "description": "Filter by health status", "name": "healthy", "in": "query" }, { "type": "integer", + "default": 1, "description": "Page number", "name": "page", "in": "query" }, { "type": "integer", - "description": "Max result per page", + "default": 10, + "description": "Results per page", "name": "size", "in": "query" } ], "responses": { "200": { - "description": "OK", + "description": "List of nodes", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/db.Node" - } + "$ref": "#/definitions/gin.H" } }, "400": { - "description": "Bad Request", - "schema": {} + "description": "Bad request", + "schema": { + "$ref": "#/definitions/gin.H" + } } } }, "post": { - "description": "register a node", + "description": "Register a new node in the system", "consumes": [ "application/json" ], "produces": [ "application/json" ], - "summary": "register a node", + "tags": [ + "nodes" + ], + "summary": "Register new node", "parameters": [ { - "description": "node id", - "name": "node_id", + "description": "Node registration data", + "name": "request", "in": "body", "required": true, "schema": { - "type": "integer" + "$ref": "#/definitions/server.NodeRegistrationRequest" } - }, - { - "description": "farm id", - "name": "farm_id", - "in": "body", - "required": true, + } + ], + "responses": { + "201": { + "description": "Node registered successfully", "schema": { - "type": "integer" + "$ref": "#/definitions/gin.H" } }, - { - "description": "twin id", - "name": "twin_id", - "in": "body", - "required": true, + "400": { + "description": "Invalid request", "schema": { - "type": "integer" + "$ref": "#/definitions/gin.H" } }, - { - "description": "node features ", - "name": "features", - "in": "body", - "required": true, + "409": { + "description": "Node already exists", "schema": { - "type": "array", - "items": { - "type": "string" - } + "$ref": "#/definitions/gin.H" } - }, + } + } + } + }, + "/nodes/{node_id}": { + "get": { + "description": "Get details for a specific node", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "nodes" + ], + "summary": "Get node details", + "parameters": [ { - "description": "node status", - "name": "status", - "in": "body", + "type": "integer", + "description": "Node ID", + "name": "node_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Node details", "schema": { - "type": "string" + "$ref": "#/definitions/gin.H" } }, - { - "description": "node healthy", - "name": "healthy", - "in": "body", + "400": { + "description": "Invalid node ID", "schema": { - "type": "boolean" + "$ref": "#/definitions/gin.H" } }, - { - "description": "node dedicated", - "name": "dedicated", - "in": "body", + "404": { + "description": "Node not found", "schema": { - "type": "boolean" + "$ref": "#/definitions/gin.H" } - }, + } + } + } + }, + "/nodes/{node_id}/uptime": { + "post": { + "description": "Submit uptime report for a node", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "nodes" + ], + "summary": "Report node uptime", + "parameters": [ { - "description": "node rented", - "name": "rented", - "in": "body", - "schema": { - "type": "boolean" - } + "type": "integer", + "description": "Node ID", + "name": "node_id", + "in": "path", + "required": true }, { - "description": "node rentable", - "name": "rentable", + "description": "Uptime report data", + "name": "request", "in": "body", + "required": true, "schema": { - "type": "boolean" + "$ref": "#/definitions/server.UptimeReportRequest" } - }, - { - "description": "price in usd", - "name": "price_usd", - "in": "body", + } + ], + "responses": { + "201": { + "description": "Uptime reported successfully", "schema": { - "type": "number" + "$ref": "#/definitions/gin.H" } }, - { - "description": "uptime report", - "name": "uptime", - "in": "body", + "400": { + "description": "Invalid request", "schema": { - "type": "integer" + "$ref": "#/definitions/gin.H" } }, - { - "description": "consumption report", - "name": "consumption", - "in": "body", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", + "404": { + "description": "Node not found", "schema": { - "$ref": "#/definitions/db.Node" + "$ref": "#/definitions/gin.H" } - }, - "400": { - "description": "Bad Request", - "schema": {} } } } }, - "/nodes/{node_id}": { + "/zos/version": { "get": { - "description": "get a node with specific id", - "consumes": [ - "application/json" - ], + "description": "Gets the ZOS version", "produces": [ "application/json" ], - "summary": "get node", - "parameters": [ - { - "type": "integer", - "description": "node id", - "name": "node_id", - "in": "path" - } + "tags": [ + "ZOS" ], + "summary": "Get ZOS Version", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/db.Node" + "$ref": "#/definitions/gin.H" } }, - "400": { - "description": "Bad Request", - "schema": {} + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/gin.H" + } } } - } - }, - "/nodes/{node_id}/uptime": { + }, "post": { - "description": "save consumption report of a node", + "description": "Sets the ZOS version", "consumes": [ "application/json" ], "produces": [ "application/json" ], - "summary": "consumption report", + "tags": [ + "ZOS" + ], + "summary": "Set ZOS Version", "parameters": [ { - "type": "integer", - "description": "node id", - "name": "node_id", - "in": "query", - "required": true - }, - { - "description": "consumption report", - "name": "consumption", + "description": "Update ZOS Version Request", + "name": "body", "in": "body", + "required": true, "schema": { - "type": "string" + "$ref": "#/definitions/server.ZOSVersionRequest" } } ], @@ -500,91 +637,395 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "string" + "$ref": "#/definitions/gin.H" } }, "400": { "description": "Bad Request", - "schema": {} + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/gin.H" + } } } } } }, "definitions": { + "db.Account": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "farms": { + "description": "Relations | likely we need to use OnDelete:RESTRICT (Prevent Twin deletion if farms exist)\n@swagger:ignore", + "type": "array", + "items": { + "$ref": "#/definitions/db.Farm" + } + }, + "publicKey": { + "description": "The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substrate echo system?\n(still SS58 can be used or plain base58 ,TBD)", + "type": "string" + }, + "relays": { + "description": "Optional list of relay domains", + "type": "array", + "items": { + "type": "string" + } + }, + "rmb_enc_key": { + "description": "Optional base64 encoded public key for rmb communication", + "type": "string" + }, + "twinID": { + "type": "integer" + }, + "updatedAt": { + "type": "string" + } + } + }, "db.Farm": { "type": "object", "properties": { + "createdAt": { + "type": "string" + }, "dedicated": { "type": "boolean" }, - "farm_free_ips": { - "type": "integer" - }, "farm_id": { - "description": "Primary key", "type": "integer" }, "farm_name": { "type": "string" }, "nodes": { + "description": "@swagger:ignore", "type": "array", "items": { "$ref": "#/definitions/db.Node" } }, "twin_id": { + "description": "Farmer account reference", "type": "integer" + }, + "updatedAt": { + "type": "string" } } }, - "db.Node": { + "db.Interface": { "type": "object", "properties": { - "consumption": { + "ips": { "type": "string" }, - "dedicated": { + "mac": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "db.Location": { + "type": "object", + "properties": { + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "latitude": { + "type": "string" + }, + "longitude": { + "type": "string" + } + } + }, + "db.Node": { + "type": "object", + "properties": { + "approved": { "type": "boolean" }, - "extra_fee": { - "type": "integer" + "createdAt": { + "type": "string" }, "farm_id": { + "description": "Constraints set to prevents unintended account deletion if linked Farms/nodes exist.", "type": "integer" }, - "features": { + "interface": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/db.Interface" } }, - "healthy": { - "type": "boolean" + "location": { + "$ref": "#/definitions/db.Location" }, "node_id": { "type": "integer" }, - "price_usd": { - "type": "number" - }, - "rentable": { - "type": "boolean" + "resources": { + "description": "PublicConfig PublicConfig ` + "`" + `json:\"public_config\" gorm:\"type:json\"` + "`" + `", + "allOf": [ + { + "$ref": "#/definitions/db.Resources" + } + ] }, - "rented": { + "secureBoot": { "type": "boolean" }, - "status": { + "serialNumber": { "type": "string" }, "twin_id": { + "description": "Node account reference", "type": "integer" }, + "updatedAt": { + "type": "string" + }, "uptime": { + "type": "array", + "items": { + "$ref": "#/definitions/db.UptimeReport" + } + }, + "virtualized": { + "type": "boolean" + } + } + }, + "db.Resources": { + "type": "object", + "properties": { + "cru": { + "type": "integer" + }, + "hru": { + "type": "integer" + }, + "mru": { + "type": "integer" + }, + "sru": { + "type": "integer" + } + } + }, + "db.UptimeReport": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "duration": { + "description": "Uptime duration for this period", + "allOf": [ + { + "$ref": "#/definitions/time.Duration" + } + ] + }, + "id": { + "type": "integer" + }, + "nodeID": { "type": "integer" + }, + "timestamp": { + "type": "string" + }, + "wasRestart": { + "description": "True if this report followed a restart", + "type": "boolean" } } + }, + "gin.H": { + "type": "object", + "additionalProperties": {} + }, + "server.AccountCreationRequest": { + "type": "object", + "required": [ + "public_key", + "signature", + "timestamp" + ], + "properties": { + "public_key": { + "description": "base64 encoded", + "type": "string" + }, + "relays": { + "type": "array", + "items": { + "type": "string" + } + }, + "rmb_enc_key": { + "type": "string" + }, + "signature": { + "description": "the registrar expect a signature of a message with format ` + "`" + `timestampStr:publicKeyBase64` + "`" + `\n- signature format: base64(ed25519_or_sr22519_signature)", + "type": "string" + }, + "timestamp": { + "type": "integer" + } + } + }, + "server.NodeRegistrationRequest": { + "type": "object", + "required": [ + "farm_id", + "interfaces", + "location", + "resources", + "serial_number", + "twin_id" + ], + "properties": { + "farm_id": { + "type": "integer", + "minimum": 1 + }, + "interfaces": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/db.Interface" + } + }, + "location": { + "$ref": "#/definitions/db.Location" + }, + "resources": { + "$ref": "#/definitions/db.Resources" + }, + "secure_boot": { + "type": "boolean" + }, + "serial_number": { + "type": "string" + }, + "twin_id": { + "type": "integer", + "minimum": 1 + }, + "virtualized": { + "type": "boolean" + } + } + }, + "server.UpdateAccountRequest": { + "type": "object", + "properties": { + "relays": { + "type": "array", + "items": { + "type": "string" + } + }, + "rmb_enc_key": { + "type": "string" + } + } + }, + "server.UpdateFarmRequest": { + "type": "object", + "required": [ + "farm_name" + ], + "properties": { + "farm_name": { + "type": "string", + "maxLength": 40, + "minLength": 1 + } + } + }, + "server.UptimeReportRequest": { + "type": "object", + "required": [ + "timestamp", + "uptime" + ], + "properties": { + "timestamp": { + "type": "string" + }, + "uptime": { + "$ref": "#/definitions/time.Duration" + } + } + }, + "server.ZOSVersionRequest": { + "type": "object", + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + } + } + }, + "time.Duration": { + "type": "integer", + "enum": [ + -9223372036854775808, + 9223372036854775807, + 1, + 1000, + 1000000, + 1000000000, + 60000000000, + 3600000000000, + 1, + 1000, + 1000000, + 1000000000, + 60000000000 + ], + "x-enum-varnames": [ + "minDuration", + "maxDuration", + "Nanosecond", + "Microsecond", + "Millisecond", + "Second", + "Minute", + "Hour", + "Nanosecond", + "Microsecond", + "Millisecond", + "Second", + "Minute" + ] } } }` diff --git a/node-registrar/docs/swagger.json b/node-registrar/docs/swagger.json index 3546425..1fed432 100644 --- a/node-registrar/docs/swagger.json +++ b/node-registrar/docs/swagger.json @@ -4,484 +4,621 @@ "contact": {} }, "paths": { - "/farm/{farm_id}": { + "/accounts": { "get": { - "description": "get a farm with specific id", + "description": "This endpoint retrieves an account by its twin ID or public key.", "consumes": [ "application/json" ], "produces": [ "application/json" ], - "summary": "get farm", + "tags": [ + "accounts" + ], + "summary": "Retrieve an account by twin ID or public key", "parameters": [ { "type": "integer", - "description": "farm id", - "name": "farm_id", + "description": "Twin ID of the account", + "name": "twin_id", + "in": "query" + }, + { + "type": "string", + "description": "Base64 decoded Public key of the account", + "name": "public_key", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Account details", + "schema": { + "$ref": "#/definitions/db.Account" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Account not found", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + }, + "post": { + "description": "Create a new twin account with cryptographic verification", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "accounts" + ], + "summary": "Create new account", + "parameters": [ + { + "description": "Account creation data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.AccountCreationRequest" + } + } + ], + "responses": { + "201": { + "description": "Created account details", + "schema": { + "$ref": "#/definitions/db.Account" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "409": { + "description": "Account already exists", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } + } + }, + "/accounts/{twin_id}": { + "patch": { + "description": "Updates an account's relays and RMB encryption key", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "accounts" + ], + "summary": "Update account details", + "parameters": [ + { + "type": "integer", + "description": "Twin ID of the account", + "name": "twin_id", "in": "path", "required": true + }, + { + "description": "Account details to update", + "name": "account", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.UpdateAccountRequest" + } } ], "responses": { "200": { - "description": "OK", + "description": "Account updated successfully", "schema": { - "$ref": "#/definitions/db.Farm" + "$ref": "#/definitions/gin.H" } }, "400": { - "description": "Bad Request", - "schema": {} + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Account not found", + "schema": { + "$ref": "#/definitions/gin.H" + } } } } }, - "/farms/": { + "/farms": { "get": { - "description": "list farms with specific filter", + "description": "Get a list of farms with optional filters", "consumes": [ "application/json" ], "produces": [ "application/json" ], - "summary": "list farms", + "tags": [ + "farms" + ], + "summary": "List farms", "parameters": [ { "type": "string", - "description": "farm name", + "description": "Filter by farm name", "name": "farm_name", "in": "query" }, { "type": "integer", - "description": "farm id", + "description": "Filter by farm ID", "name": "farm_id", "in": "query" }, { "type": "integer", - "description": "twin id", + "description": "Filter by twin ID", "name": "twin_id", "in": "query" }, { "type": "integer", + "default": 1, "description": "Page number", "name": "page", "in": "query" }, { "type": "integer", - "description": "Max result per page", + "default": 10, + "description": "Results per page", "name": "size", "in": "query" } ], "responses": { "200": { - "description": "OK", + "description": "List of farms", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/db.Farm" - } + "$ref": "#/definitions/gin.H" } }, "400": { - "description": "Bad Request", - "schema": {} + "description": "Bad request", + "schema": { + "$ref": "#/definitions/gin.H" + } } } }, "post": { - "description": "creates a farm", + "description": "Create a new farm entry", "consumes": [ "application/json" ], "produces": [ "application/json" ], - "summary": "create a farm", + "tags": [ + "farms" + ], + "summary": "Create new farm", "parameters": [ { - "description": "farm id", - "name": "farm_id", + "description": "Farm creation data", + "name": "farm", "in": "body", "required": true, "schema": { - "type": "integer" + "$ref": "#/definitions/db.Farm" } - }, - { - "description": "farm name", - "name": "farm_name", - "in": "body", - "required": true, + } + ], + "responses": { + "201": { + "description": "Farm created successfully", "schema": { - "type": "integer" + "$ref": "#/definitions/gin.H" } }, - { - "description": "twin id", - "name": "twin_id", - "in": "body", - "required": true, + "400": { + "description": "Invalid request", "schema": { - "type": "integer" + "$ref": "#/definitions/gin.H" } }, - { - "description": "dedicated farm", - "name": "dedicated", - "in": "body", + "409": { + "description": "Farm already exists", "schema": { - "type": "boolean" + "$ref": "#/definitions/gin.H" } - }, + } + } + } + }, + "/farms/{farm_id}": { + "get": { + "description": "Get details for a specific farm", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "farms" + ], + "summary": "Get farm details", + "parameters": [ { - "description": "farm free ips", - "name": "farm_free_ips", - "in": "body", - "schema": { - "type": "integer" - } + "type": "integer", + "description": "Farm ID", + "name": "farm_id", + "in": "path", + "required": true } ], "responses": { "200": { - "description": "OK", + "description": "Farm details", "schema": { - "$ref": "#/definitions/db.Farm" + "$ref": "#/definitions/gin.H" } }, "400": { - "description": "Bad Request", - "schema": {} + "description": "Invalid farm ID", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Farm not found", + "schema": { + "$ref": "#/definitions/gin.H" + } } } }, "patch": { - "description": "update a farm", + "description": "Update existing farm details", "consumes": [ "application/json" ], "produces": [ "application/json" ], - "summary": "update a farm", + "tags": [ + "farms" + ], + "summary": "Update farm", "parameters": [ { - "description": "farm id", + "type": "integer", + "description": "Farm ID", "name": "farm_id", - "in": "body", - "required": true, - "schema": { - "type": "integer" - } - }, - { - "description": "farm name", - "name": "farm_name", - "in": "body", - "schema": { - "type": "integer" - } - }, - { - "description": "twin id", - "name": "twin_id", - "in": "body", - "schema": { - "type": "integer" - } - }, - { - "description": "dedicated farm", - "name": "dedicated", - "in": "body", - "schema": { - "type": "boolean" - } + "in": "path", + "required": true }, { - "description": "farm free ips", - "name": "farm_free_ips", + "description": "Farm update data", + "name": "request", "in": "body", + "required": true, "schema": { - "type": "integer" + "$ref": "#/definitions/server.UpdateFarmRequest" } } ], "responses": { "200": { - "description": "OK", + "description": "Farm updated successfully", "schema": { - "$ref": "#/definitions/db.Farm" + "$ref": "#/definitions/gin.H" } }, "400": { - "description": "Bad Request", - "schema": {} + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Farm not found", + "schema": { + "$ref": "#/definitions/gin.H" + } } } } }, - "/nodes/": { + "/nodes": { "get": { - "description": "list nodes with specific filter", + "description": "Get a list of nodes with optional filters", "consumes": [ "application/json" ], "produces": [ "application/json" ], - "summary": "list nodes", + "tags": [ + "nodes" + ], + "summary": "List nodes", "parameters": [ { "type": "integer", - "description": "node id", + "description": "Filter by node ID", "name": "node_id", "in": "query" }, { "type": "integer", - "description": "farm id", + "description": "Filter by farm ID", "name": "farm_id", "in": "query" }, { "type": "integer", - "description": "twin id", + "description": "Filter by twin ID", "name": "twin_id", "in": "query" }, { "type": "string", - "description": "node status", + "description": "Filter by status", "name": "status", "in": "query" }, { "type": "boolean", - "description": "is node healthy", + "description": "Filter by health status", "name": "healthy", "in": "query" }, { "type": "integer", + "default": 1, "description": "Page number", "name": "page", "in": "query" }, { "type": "integer", - "description": "Max result per page", + "default": 10, + "description": "Results per page", "name": "size", "in": "query" } ], "responses": { "200": { - "description": "OK", + "description": "List of nodes", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/db.Node" - } + "$ref": "#/definitions/gin.H" } }, "400": { - "description": "Bad Request", - "schema": {} + "description": "Bad request", + "schema": { + "$ref": "#/definitions/gin.H" + } } } }, "post": { - "description": "register a node", + "description": "Register a new node in the system", "consumes": [ "application/json" ], "produces": [ "application/json" ], - "summary": "register a node", + "tags": [ + "nodes" + ], + "summary": "Register new node", "parameters": [ { - "description": "node id", - "name": "node_id", + "description": "Node registration data", + "name": "request", "in": "body", "required": true, "schema": { - "type": "integer" + "$ref": "#/definitions/server.NodeRegistrationRequest" } - }, - { - "description": "farm id", - "name": "farm_id", - "in": "body", - "required": true, + } + ], + "responses": { + "201": { + "description": "Node registered successfully", "schema": { - "type": "integer" + "$ref": "#/definitions/gin.H" } }, - { - "description": "twin id", - "name": "twin_id", - "in": "body", - "required": true, + "400": { + "description": "Invalid request", "schema": { - "type": "integer" + "$ref": "#/definitions/gin.H" } }, - { - "description": "node features ", - "name": "features", - "in": "body", - "required": true, + "409": { + "description": "Node already exists", "schema": { - "type": "array", - "items": { - "type": "string" - } + "$ref": "#/definitions/gin.H" } - }, + } + } + } + }, + "/nodes/{node_id}": { + "get": { + "description": "Get details for a specific node", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "nodes" + ], + "summary": "Get node details", + "parameters": [ { - "description": "node status", - "name": "status", - "in": "body", + "type": "integer", + "description": "Node ID", + "name": "node_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Node details", "schema": { - "type": "string" + "$ref": "#/definitions/gin.H" } }, - { - "description": "node healthy", - "name": "healthy", - "in": "body", + "400": { + "description": "Invalid node ID", "schema": { - "type": "boolean" + "$ref": "#/definitions/gin.H" } }, - { - "description": "node dedicated", - "name": "dedicated", - "in": "body", + "404": { + "description": "Node not found", "schema": { - "type": "boolean" + "$ref": "#/definitions/gin.H" } - }, + } + } + } + }, + "/nodes/{node_id}/uptime": { + "post": { + "description": "Submit uptime report for a node", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "nodes" + ], + "summary": "Report node uptime", + "parameters": [ { - "description": "node rented", - "name": "rented", - "in": "body", - "schema": { - "type": "boolean" - } + "type": "integer", + "description": "Node ID", + "name": "node_id", + "in": "path", + "required": true }, { - "description": "node rentable", - "name": "rentable", + "description": "Uptime report data", + "name": "request", "in": "body", + "required": true, "schema": { - "type": "boolean" + "$ref": "#/definitions/server.UptimeReportRequest" } - }, - { - "description": "price in usd", - "name": "price_usd", - "in": "body", + } + ], + "responses": { + "201": { + "description": "Uptime reported successfully", "schema": { - "type": "number" + "$ref": "#/definitions/gin.H" } }, - { - "description": "uptime report", - "name": "uptime", - "in": "body", + "400": { + "description": "Invalid request", "schema": { - "type": "integer" + "$ref": "#/definitions/gin.H" } }, - { - "description": "consumption report", - "name": "consumption", - "in": "body", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", + "404": { + "description": "Node not found", "schema": { - "$ref": "#/definitions/db.Node" + "$ref": "#/definitions/gin.H" } - }, - "400": { - "description": "Bad Request", - "schema": {} } } } }, - "/nodes/{node_id}": { + "/zos/version": { "get": { - "description": "get a node with specific id", - "consumes": [ - "application/json" - ], + "description": "Gets the ZOS version", "produces": [ "application/json" ], - "summary": "get node", - "parameters": [ - { - "type": "integer", - "description": "node id", - "name": "node_id", - "in": "path" - } + "tags": [ + "ZOS" ], + "summary": "Get ZOS Version", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/db.Node" + "$ref": "#/definitions/gin.H" } }, - "400": { - "description": "Bad Request", - "schema": {} + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/gin.H" + } } } - } - }, - "/nodes/{node_id}/uptime": { + }, "post": { - "description": "save consumption report of a node", + "description": "Sets the ZOS version", "consumes": [ "application/json" ], "produces": [ "application/json" ], - "summary": "consumption report", + "tags": [ + "ZOS" + ], + "summary": "Set ZOS Version", "parameters": [ { - "type": "integer", - "description": "node id", - "name": "node_id", - "in": "query", - "required": true - }, - { - "description": "consumption report", - "name": "consumption", + "description": "Update ZOS Version Request", + "name": "body", "in": "body", + "required": true, "schema": { - "type": "string" + "$ref": "#/definitions/server.ZOSVersionRequest" } } ], @@ -489,91 +626,395 @@ "200": { "description": "OK", "schema": { - "type": "string" + "$ref": "#/definitions/gin.H" } }, "400": { "description": "Bad Request", - "schema": {} + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/gin.H" + } } } } } }, "definitions": { + "db.Account": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "farms": { + "description": "Relations | likely we need to use OnDelete:RESTRICT (Prevent Twin deletion if farms exist)\n@swagger:ignore", + "type": "array", + "items": { + "$ref": "#/definitions/db.Farm" + } + }, + "publicKey": { + "description": "The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substrate echo system?\n(still SS58 can be used or plain base58 ,TBD)", + "type": "string" + }, + "relays": { + "description": "Optional list of relay domains", + "type": "array", + "items": { + "type": "string" + } + }, + "rmb_enc_key": { + "description": "Optional base64 encoded public key for rmb communication", + "type": "string" + }, + "twinID": { + "type": "integer" + }, + "updatedAt": { + "type": "string" + } + } + }, "db.Farm": { "type": "object", "properties": { + "createdAt": { + "type": "string" + }, "dedicated": { "type": "boolean" }, - "farm_free_ips": { - "type": "integer" - }, "farm_id": { - "description": "Primary key", "type": "integer" }, "farm_name": { "type": "string" }, "nodes": { + "description": "@swagger:ignore", "type": "array", "items": { "$ref": "#/definitions/db.Node" } }, "twin_id": { + "description": "Farmer account reference", "type": "integer" + }, + "updatedAt": { + "type": "string" } } }, - "db.Node": { + "db.Interface": { "type": "object", "properties": { - "consumption": { + "ips": { "type": "string" }, - "dedicated": { + "mac": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "db.Location": { + "type": "object", + "properties": { + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "latitude": { + "type": "string" + }, + "longitude": { + "type": "string" + } + } + }, + "db.Node": { + "type": "object", + "properties": { + "approved": { "type": "boolean" }, - "extra_fee": { - "type": "integer" + "createdAt": { + "type": "string" }, "farm_id": { + "description": "Constraints set to prevents unintended account deletion if linked Farms/nodes exist.", "type": "integer" }, - "features": { + "interface": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/db.Interface" } }, - "healthy": { - "type": "boolean" + "location": { + "$ref": "#/definitions/db.Location" }, "node_id": { "type": "integer" }, - "price_usd": { - "type": "number" - }, - "rentable": { - "type": "boolean" + "resources": { + "description": "PublicConfig PublicConfig `json:\"public_config\" gorm:\"type:json\"`", + "allOf": [ + { + "$ref": "#/definitions/db.Resources" + } + ] }, - "rented": { + "secureBoot": { "type": "boolean" }, - "status": { + "serialNumber": { "type": "string" }, "twin_id": { + "description": "Node account reference", "type": "integer" }, + "updatedAt": { + "type": "string" + }, "uptime": { + "type": "array", + "items": { + "$ref": "#/definitions/db.UptimeReport" + } + }, + "virtualized": { + "type": "boolean" + } + } + }, + "db.Resources": { + "type": "object", + "properties": { + "cru": { + "type": "integer" + }, + "hru": { + "type": "integer" + }, + "mru": { + "type": "integer" + }, + "sru": { + "type": "integer" + } + } + }, + "db.UptimeReport": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "duration": { + "description": "Uptime duration for this period", + "allOf": [ + { + "$ref": "#/definitions/time.Duration" + } + ] + }, + "id": { + "type": "integer" + }, + "nodeID": { "type": "integer" + }, + "timestamp": { + "type": "string" + }, + "wasRestart": { + "description": "True if this report followed a restart", + "type": "boolean" } } + }, + "gin.H": { + "type": "object", + "additionalProperties": {} + }, + "server.AccountCreationRequest": { + "type": "object", + "required": [ + "public_key", + "signature", + "timestamp" + ], + "properties": { + "public_key": { + "description": "base64 encoded", + "type": "string" + }, + "relays": { + "type": "array", + "items": { + "type": "string" + } + }, + "rmb_enc_key": { + "type": "string" + }, + "signature": { + "description": "the registrar expect a signature of a message with format `timestampStr:publicKeyBase64`\n- signature format: base64(ed25519_or_sr22519_signature)", + "type": "string" + }, + "timestamp": { + "type": "integer" + } + } + }, + "server.NodeRegistrationRequest": { + "type": "object", + "required": [ + "farm_id", + "interfaces", + "location", + "resources", + "serial_number", + "twin_id" + ], + "properties": { + "farm_id": { + "type": "integer", + "minimum": 1 + }, + "interfaces": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/db.Interface" + } + }, + "location": { + "$ref": "#/definitions/db.Location" + }, + "resources": { + "$ref": "#/definitions/db.Resources" + }, + "secure_boot": { + "type": "boolean" + }, + "serial_number": { + "type": "string" + }, + "twin_id": { + "type": "integer", + "minimum": 1 + }, + "virtualized": { + "type": "boolean" + } + } + }, + "server.UpdateAccountRequest": { + "type": "object", + "properties": { + "relays": { + "type": "array", + "items": { + "type": "string" + } + }, + "rmb_enc_key": { + "type": "string" + } + } + }, + "server.UpdateFarmRequest": { + "type": "object", + "required": [ + "farm_name" + ], + "properties": { + "farm_name": { + "type": "string", + "maxLength": 40, + "minLength": 1 + } + } + }, + "server.UptimeReportRequest": { + "type": "object", + "required": [ + "timestamp", + "uptime" + ], + "properties": { + "timestamp": { + "type": "string" + }, + "uptime": { + "$ref": "#/definitions/time.Duration" + } + } + }, + "server.ZOSVersionRequest": { + "type": "object", + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + } + } + }, + "time.Duration": { + "type": "integer", + "enum": [ + -9223372036854775808, + 9223372036854775807, + 1, + 1000, + 1000000, + 1000000000, + 60000000000, + 3600000000000, + 1, + 1000, + 1000000, + 1000000000, + 60000000000 + ], + "x-enum-varnames": [ + "minDuration", + "maxDuration", + "Nanosecond", + "Microsecond", + "Millisecond", + "Second", + "Minute", + "Hour", + "Nanosecond", + "Microsecond", + "Millisecond", + "Second", + "Minute" + ] } } } \ No newline at end of file diff --git a/node-registrar/docs/swagger.yaml b/node-registrar/docs/swagger.yaml index 39ed5c4..1b133dd 100644 --- a/node-registrar/docs/swagger.yaml +++ b/node-registrar/docs/swagger.yaml @@ -1,101 +1,387 @@ definitions: + db.Account: + properties: + createdAt: + type: string + farms: + description: |- + Relations | likely we need to use OnDelete:RESTRICT (Prevent Twin deletion if farms exist) + @swagger:ignore + items: + $ref: '#/definitions/db.Farm' + type: array + publicKey: + description: |- + The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substrate echo system? + (still SS58 can be used or plain base58 ,TBD) + type: string + relays: + description: Optional list of relay domains + items: + type: string + type: array + rmb_enc_key: + description: Optional base64 encoded public key for rmb communication + type: string + twinID: + type: integer + updatedAt: + type: string + type: object db.Farm: properties: + createdAt: + type: string dedicated: type: boolean - farm_free_ips: - type: integer farm_id: - description: Primary key type: integer farm_name: type: string nodes: + description: '@swagger:ignore' items: $ref: '#/definitions/db.Node' type: array twin_id: + description: Farmer account reference type: integer + updatedAt: + type: string type: object - db.Node: + db.Interface: properties: - consumption: + ips: type: string - dedicated: + mac: + type: string + name: + type: string + type: object + db.Location: + properties: + city: + type: string + country: + type: string + latitude: + type: string + longitude: + type: string + type: object + db.Node: + properties: + approved: type: boolean - extra_fee: - type: integer + createdAt: + type: string farm_id: + description: Constraints set to prevents unintended account deletion if linked + Farms/nodes exist. type: integer - features: + interface: items: - type: string + $ref: '#/definitions/db.Interface' type: array - healthy: - type: boolean + location: + $ref: '#/definitions/db.Location' node_id: type: integer - price_usd: - type: number - rentable: + resources: + allOf: + - $ref: '#/definitions/db.Resources' + description: PublicConfig PublicConfig `json:"public_config" gorm:"type:json"` + secureBoot: type: boolean - rented: - type: boolean - status: + serialNumber: type: string twin_id: + description: Node account reference type: integer + updatedAt: + type: string uptime: + items: + $ref: '#/definitions/db.UptimeReport' + type: array + virtualized: + type: boolean + type: object + db.Resources: + properties: + cru: + type: integer + hru: + type: integer + mru: + type: integer + sru: + type: integer + type: object + db.UptimeReport: + properties: + createdAt: + type: string + duration: + allOf: + - $ref: '#/definitions/time.Duration' + description: Uptime duration for this period + id: + type: integer + nodeID: + type: integer + timestamp: + type: string + wasRestart: + description: True if this report followed a restart + type: boolean + type: object + gin.H: + additionalProperties: {} + type: object + server.AccountCreationRequest: + properties: + public_key: + description: base64 encoded + type: string + relays: + items: + type: string + type: array + rmb_enc_key: + type: string + signature: + description: |- + the registrar expect a signature of a message with format `timestampStr:publicKeyBase64` + - signature format: base64(ed25519_or_sr22519_signature) + type: string + timestamp: + type: integer + required: + - public_key + - signature + - timestamp + type: object + server.NodeRegistrationRequest: + properties: + farm_id: + minimum: 1 + type: integer + interfaces: + items: + $ref: '#/definitions/db.Interface' + minItems: 1 + type: array + location: + $ref: '#/definitions/db.Location' + resources: + $ref: '#/definitions/db.Resources' + secure_boot: + type: boolean + serial_number: + type: string + twin_id: + minimum: 1 type: integer + virtualized: + type: boolean + required: + - farm_id + - interfaces + - location + - resources + - serial_number + - twin_id + type: object + server.UpdateAccountRequest: + properties: + relays: + items: + type: string + type: array + rmb_enc_key: + type: string + type: object + server.UpdateFarmRequest: + properties: + farm_name: + maxLength: 40 + minLength: 1 + type: string + required: + - farm_name type: object + server.UptimeReportRequest: + properties: + timestamp: + type: string + uptime: + $ref: '#/definitions/time.Duration' + required: + - timestamp + - uptime + type: object + server.ZOSVersionRequest: + properties: + version: + type: string + required: + - version + type: object + time.Duration: + enum: + - -9223372036854775808 + - 9223372036854775807 + - 1 + - 1000 + - 1000000 + - 1000000000 + - 60000000000 + - 3600000000000 + - 1 + - 1000 + - 1000000 + - 1000000000 + - 60000000000 + type: integer + x-enum-varnames: + - minDuration + - maxDuration + - Nanosecond + - Microsecond + - Millisecond + - Second + - Minute + - Hour + - Nanosecond + - Microsecond + - Millisecond + - Second + - Minute info: contact: {} paths: - /farm/{farm_id}: + /accounts: get: consumes: - application/json - description: get a farm with specific id + description: This endpoint retrieves an account by its twin ID or public key. + parameters: + - description: Twin ID of the account + in: query + name: twin_id + type: integer + - description: Base64 decoded Public key of the account + in: query + name: public_key + type: string + produces: + - application/json + responses: + "200": + description: Account details + schema: + $ref: '#/definitions/db.Account' + "400": + description: Invalid request + schema: + $ref: '#/definitions/gin.H' + "404": + description: Account not found + schema: + $ref: '#/definitions/gin.H' + summary: Retrieve an account by twin ID or public key + tags: + - accounts + post: + consumes: + - application/json + description: Create a new twin account with cryptographic verification + parameters: + - description: Account creation data + in: body + name: request + required: true + schema: + $ref: '#/definitions/server.AccountCreationRequest' + produces: + - application/json + responses: + "201": + description: Created account details + schema: + $ref: '#/definitions/db.Account' + "400": + description: Invalid request + schema: + $ref: '#/definitions/gin.H' + "409": + description: Account already exists + schema: + $ref: '#/definitions/gin.H' + summary: Create new account + tags: + - accounts + /accounts/{twin_id}: + patch: + consumes: + - application/json + description: Updates an account's relays and RMB encryption key parameters: - - description: farm id + - description: Twin ID of the account in: path - name: farm_id + name: twin_id required: true type: integer + - description: Account details to update + in: body + name: account + required: true + schema: + $ref: '#/definitions/server.UpdateAccountRequest' produces: - application/json responses: "200": - description: OK + description: Account updated successfully schema: - $ref: '#/definitions/db.Farm' + $ref: '#/definitions/gin.H' "400": - description: Bad Request - schema: {} - summary: get farm - /farms/: + description: Invalid request + schema: + $ref: '#/definitions/gin.H' + "404": + description: Account not found + schema: + $ref: '#/definitions/gin.H' + summary: Update account details + tags: + - accounts + /farms: get: consumes: - application/json - description: list farms with specific filter + description: Get a list of farms with optional filters parameters: - - description: farm name + - description: Filter by farm name in: query name: farm_name type: string - - description: farm id + - description: Filter by farm ID in: query name: farm_id type: integer - - description: twin id + - description: Filter by twin ID in: query name: twin_id type: integer - - description: Page number + - default: 1 + description: Page number in: query name: page type: integer - - description: Max result per page + - default: 10 + description: Results per page in: query name: size type: integer @@ -103,132 +389,141 @@ paths: - application/json responses: "200": - description: OK + description: List of farms schema: - items: - $ref: '#/definitions/db.Farm' - type: array + $ref: '#/definitions/gin.H' "400": - description: Bad Request - schema: {} - summary: list farms - patch: + description: Bad request + schema: + $ref: '#/definitions/gin.H' + summary: List farms + tags: + - farms + post: consumes: - application/json - description: update a farm + description: Create a new farm entry parameters: - - description: farm id + - description: Farm creation data in: body - name: farm_id + name: farm required: true schema: - type: integer - - description: farm name - in: body - name: farm_name - schema: - type: integer - - description: twin id - in: body - name: twin_id - schema: - type: integer - - description: dedicated farm - in: body - name: dedicated - schema: - type: boolean - - description: farm free ips - in: body - name: farm_free_ips - schema: - type: integer + $ref: '#/definitions/db.Farm' produces: - application/json responses: - "200": - description: OK + "201": + description: Farm created successfully schema: - $ref: '#/definitions/db.Farm' + $ref: '#/definitions/gin.H' "400": - description: Bad Request - schema: {} - summary: update a farm - post: + description: Invalid request + schema: + $ref: '#/definitions/gin.H' + "409": + description: Farm already exists + schema: + $ref: '#/definitions/gin.H' + summary: Create new farm + tags: + - farms + /farms/{farm_id}: + get: consumes: - application/json - description: creates a farm + description: Get details for a specific farm parameters: - - description: farm id - in: body + - description: Farm ID + in: path name: farm_id required: true - schema: - type: integer - - description: farm name - in: body - name: farm_name + type: integer + produces: + - application/json + responses: + "200": + description: Farm details + schema: + $ref: '#/definitions/gin.H' + "400": + description: Invalid farm ID + schema: + $ref: '#/definitions/gin.H' + "404": + description: Farm not found + schema: + $ref: '#/definitions/gin.H' + summary: Get farm details + tags: + - farms + patch: + consumes: + - application/json + description: Update existing farm details + parameters: + - description: Farm ID + in: path + name: farm_id required: true - schema: - type: integer - - description: twin id + type: integer + - description: Farm update data in: body - name: twin_id + name: request required: true schema: - type: integer - - description: dedicated farm - in: body - name: dedicated - schema: - type: boolean - - description: farm free ips - in: body - name: farm_free_ips - schema: - type: integer + $ref: '#/definitions/server.UpdateFarmRequest' produces: - application/json responses: "200": - description: OK + description: Farm updated successfully schema: - $ref: '#/definitions/db.Farm' + $ref: '#/definitions/gin.H' "400": - description: Bad Request - schema: {} - summary: create a farm - /nodes/: + description: Invalid request + schema: + $ref: '#/definitions/gin.H' + "404": + description: Farm not found + schema: + $ref: '#/definitions/gin.H' + summary: Update farm + tags: + - farms + /nodes: get: consumes: - application/json - description: list nodes with specific filter + description: Get a list of nodes with optional filters parameters: - - description: node id + - description: Filter by node ID in: query name: node_id type: integer - - description: farm id + - description: Filter by farm ID in: query name: farm_id type: integer - - description: twin id + - description: Filter by twin ID in: query name: twin_id type: integer - - description: node status + - description: Filter by status in: query name: status type: string - - description: is node healthy + - description: Filter by health status in: query name: healthy type: boolean - - description: Page number + - default: 1 + description: Page number in: query name: page type: integer - - description: Max result per page + - default: 10 + description: Results per page in: query name: size type: integer @@ -236,143 +531,161 @@ paths: - application/json responses: "200": - description: OK + description: List of nodes schema: - items: - $ref: '#/definitions/db.Node' - type: array + $ref: '#/definitions/gin.H' "400": - description: Bad Request - schema: {} - summary: list nodes + description: Bad request + schema: + $ref: '#/definitions/gin.H' + summary: List nodes + tags: + - nodes post: consumes: - application/json - description: register a node + description: Register a new node in the system parameters: - - description: node id - in: body - name: node_id - required: true - schema: - type: integer - - description: farm id - in: body - name: farm_id - required: true - schema: - type: integer - - description: twin id + - description: Node registration data in: body - name: twin_id - required: true - schema: - type: integer - - description: 'node features ' - in: body - name: features + name: request required: true schema: - items: - type: string - type: array - - description: node status - in: body - name: status - schema: - type: string - - description: node healthy - in: body - name: healthy - schema: - type: boolean - - description: node dedicated - in: body - name: dedicated - schema: - type: boolean - - description: node rented - in: body - name: rented - schema: - type: boolean - - description: node rentable - in: body - name: rentable - schema: - type: boolean - - description: price in usd - in: body - name: price_usd - schema: - type: number - - description: uptime report - in: body - name: uptime - schema: - type: integer - - description: consumption report - in: body - name: consumption - schema: - type: string + $ref: '#/definitions/server.NodeRegistrationRequest' produces: - application/json responses: - "200": - description: OK + "201": + description: Node registered successfully schema: - $ref: '#/definitions/db.Node' + $ref: '#/definitions/gin.H' "400": - description: Bad Request - schema: {} - summary: register a node + description: Invalid request + schema: + $ref: '#/definitions/gin.H' + "409": + description: Node already exists + schema: + $ref: '#/definitions/gin.H' + summary: Register new node + tags: + - nodes /nodes/{node_id}: get: consumes: - application/json - description: get a node with specific id + description: Get details for a specific node parameters: - - description: node id + - description: Node ID in: path name: node_id + required: true type: integer produces: - application/json responses: "200": - description: OK + description: Node details schema: - $ref: '#/definitions/db.Node' + $ref: '#/definitions/gin.H' "400": - description: Bad Request - schema: {} - summary: get node + description: Invalid node ID + schema: + $ref: '#/definitions/gin.H' + "404": + description: Node not found + schema: + $ref: '#/definitions/gin.H' + summary: Get node details + tags: + - nodes /nodes/{node_id}/uptime: post: consumes: - application/json - description: save consumption report of a node + description: Submit uptime report for a node parameters: - - description: node id - in: query + - description: Node ID + in: path name: node_id required: true type: integer - - description: consumption report + - description: Uptime report data in: body - name: consumption + name: request + required: true schema: - type: string + $ref: '#/definitions/server.UptimeReportRequest' + produces: + - application/json + responses: + "201": + description: Uptime reported successfully + schema: + $ref: '#/definitions/gin.H' + "400": + description: Invalid request + schema: + $ref: '#/definitions/gin.H' + "404": + description: Node not found + schema: + $ref: '#/definitions/gin.H' + summary: Report node uptime + tags: + - nodes + /zos/version: + get: + description: Gets the ZOS version + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/gin.H' + "404": + description: Not Found + schema: + $ref: '#/definitions/gin.H' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/gin.H' + summary: Get ZOS Version + tags: + - ZOS + post: + consumes: + - application/json + description: Sets the ZOS version + parameters: + - description: Update ZOS Version Request + in: body + name: body + required: true + schema: + $ref: '#/definitions/server.ZOSVersionRequest' produces: - application/json responses: "200": description: OK schema: - type: string + $ref: '#/definitions/gin.H' "400": description: Bad Request - schema: {} - summary: consumption report + schema: + $ref: '#/definitions/gin.H' + "409": + description: Conflict + schema: + $ref: '#/definitions/gin.H' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/gin.H' + summary: Set ZOS Version + tags: + - ZOS swagger: "2.0" From 3d82f79ed2254c7f16a4847d0f3058a8a29ca897 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Wed, 29 Jan 2025 16:58:04 +0200 Subject: [PATCH 20/31] fix missing API version from routes url and missing auth haeder from swagger UI --- node-registrar/docs/docs.go | 163 ++++++++++++++++++++++---- node-registrar/docs/swagger.json | 161 ++++++++++++++++++++++--- node-registrar/docs/swagger.yaml | 117 +++++++++++++++--- node-registrar/pkg/server/handlers.go | 23 ++++ 4 files changed, 411 insertions(+), 53 deletions(-) diff --git a/node-registrar/docs/docs.go b/node-registrar/docs/docs.go index 0b137f7..dd4519d 100644 --- a/node-registrar/docs/docs.go +++ b/node-registrar/docs/docs.go @@ -122,6 +122,13 @@ const docTemplate = `{ ], "summary": "Update account details", "parameters": [ + { + "type": "string", + "description": "Authentication format: Base64(\u003cunix_timestamp\u003e:\u003ctwin_id\u003e):Base64(signature)", + "name": "X-Auth", + "in": "header", + "required": true + }, { "type": "integer", "description": "Twin ID of the account", @@ -236,6 +243,13 @@ const docTemplate = `{ ], "summary": "Create new farm", "parameters": [ + { + "type": "string", + "description": "Authentication format: Base64(\u003cunix_timestamp\u003e:\u003ctwin_id\u003e):Base64(signature)", + "name": "X-Auth", + "in": "header", + "required": true + }, { "description": "Farm creation data", "name": "farm", @@ -324,6 +338,13 @@ const docTemplate = `{ ], "summary": "Update farm", "parameters": [ + { + "type": "string", + "description": "Authentication format: Base64(\u003cunix_timestamp\u003e:\u003ctwin_id\u003e):Base64(signature)", + "name": "X-Auth", + "in": "header", + "required": true + }, { "type": "integer", "description": "Farm ID", @@ -450,6 +471,13 @@ const docTemplate = `{ ], "summary": "Register new node", "parameters": [ + { + "type": "string", + "description": "Authentication format: Base64(\u003cunix_timestamp\u003e:\u003ctwin_id\u003e):Base64(signature)", + "name": "X-Auth", + "in": "header", + "required": true + }, { "description": "Node registration data", "name": "request", @@ -524,6 +552,64 @@ const docTemplate = `{ } } } + }, + "patch": { + "description": "Update existing node details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "nodes" + ], + "summary": "Update node", + "parameters": [ + { + "type": "string", + "description": "Authentication format: Base64(\u003cunix_timestamp\u003e:\u003ctwin_id\u003e):Base64(signature)", + "name": "X-Auth", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Node ID", + "name": "node_id", + "in": "path", + "required": true + }, + { + "description": "Node update data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.UpdateNodeRequest" + } + } + ], + "responses": { + "200": { + "description": "Node updated successfully", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Node not found", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } } }, "/nodes/{node_id}/uptime": { @@ -540,6 +626,13 @@ const docTemplate = `{ ], "summary": "Report node uptime", "parameters": [ + { + "type": "string", + "description": "Authentication format: Base64(\u003cunix_timestamp\u003e:\u003ctwin_id\u003e):Base64(signature)", + "name": "X-Auth", + "in": "header", + "required": true + }, { "type": "integer", "description": "Node ID", @@ -623,6 +716,13 @@ const docTemplate = `{ ], "summary": "Set ZOS Version", "parameters": [ + { + "type": "string", + "description": "Authentication format: Base64(\u003cunix_timestamp\u003e:\u003ctwin_id\u003e):Base64(signature)", + "name": "X-Auth", + "in": "header", + "required": true + }, { "description": "Update ZOS Version Request", "name": "body", @@ -968,6 +1068,45 @@ const docTemplate = `{ } } }, + "server.UpdateNodeRequest": { + "type": "object", + "required": [ + "farm_id", + "interfaces", + "location", + "resources", + "secure_boot", + "serial_number", + "virtualized" + ], + "properties": { + "farm_id": { + "type": "integer", + "minimum": 1 + }, + "interfaces": { + "type": "array", + "items": { + "$ref": "#/definitions/db.Interface" + } + }, + "location": { + "$ref": "#/definitions/db.Location" + }, + "resources": { + "$ref": "#/definitions/db.Resources" + }, + "secure_boot": { + "type": "boolean" + }, + "serial_number": { + "type": "string" + }, + "virtualized": { + "type": "boolean" + } + } + }, "server.UptimeReportRequest": { "type": "object", "required": [ @@ -997,14 +1136,6 @@ const docTemplate = `{ "time.Duration": { "type": "integer", "enum": [ - -9223372036854775808, - 9223372036854775807, - 1, - 1000, - 1000000, - 1000000000, - 60000000000, - 3600000000000, 1, 1000, 1000000, @@ -1012,14 +1143,6 @@ const docTemplate = `{ 60000000000 ], "x-enum-varnames": [ - "minDuration", - "maxDuration", - "Nanosecond", - "Microsecond", - "Millisecond", - "Second", - "Minute", - "Hour", "Nanosecond", "Microsecond", "Millisecond", @@ -1032,12 +1155,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 1fed432..d76bdf7 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": { @@ -111,6 +115,13 @@ ], "summary": "Update account details", "parameters": [ + { + "type": "string", + "description": "Authentication format: Base64(\u003cunix_timestamp\u003e:\u003ctwin_id\u003e):Base64(signature)", + "name": "X-Auth", + "in": "header", + "required": true + }, { "type": "integer", "description": "Twin ID of the account", @@ -225,6 +236,13 @@ ], "summary": "Create new farm", "parameters": [ + { + "type": "string", + "description": "Authentication format: Base64(\u003cunix_timestamp\u003e:\u003ctwin_id\u003e):Base64(signature)", + "name": "X-Auth", + "in": "header", + "required": true + }, { "description": "Farm creation data", "name": "farm", @@ -313,6 +331,13 @@ ], "summary": "Update farm", "parameters": [ + { + "type": "string", + "description": "Authentication format: Base64(\u003cunix_timestamp\u003e:\u003ctwin_id\u003e):Base64(signature)", + "name": "X-Auth", + "in": "header", + "required": true + }, { "type": "integer", "description": "Farm ID", @@ -439,6 +464,13 @@ ], "summary": "Register new node", "parameters": [ + { + "type": "string", + "description": "Authentication format: Base64(\u003cunix_timestamp\u003e:\u003ctwin_id\u003e):Base64(signature)", + "name": "X-Auth", + "in": "header", + "required": true + }, { "description": "Node registration data", "name": "request", @@ -513,6 +545,64 @@ } } } + }, + "patch": { + "description": "Update existing node details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "nodes" + ], + "summary": "Update node", + "parameters": [ + { + "type": "string", + "description": "Authentication format: Base64(\u003cunix_timestamp\u003e:\u003ctwin_id\u003e):Base64(signature)", + "name": "X-Auth", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Node ID", + "name": "node_id", + "in": "path", + "required": true + }, + { + "description": "Node update data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/server.UpdateNodeRequest" + } + } + ], + "responses": { + "200": { + "description": "Node updated successfully", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "404": { + "description": "Node not found", + "schema": { + "$ref": "#/definitions/gin.H" + } + } + } } }, "/nodes/{node_id}/uptime": { @@ -529,6 +619,13 @@ ], "summary": "Report node uptime", "parameters": [ + { + "type": "string", + "description": "Authentication format: Base64(\u003cunix_timestamp\u003e:\u003ctwin_id\u003e):Base64(signature)", + "name": "X-Auth", + "in": "header", + "required": true + }, { "type": "integer", "description": "Node ID", @@ -612,6 +709,13 @@ ], "summary": "Set ZOS Version", "parameters": [ + { + "type": "string", + "description": "Authentication format: Base64(\u003cunix_timestamp\u003e:\u003ctwin_id\u003e):Base64(signature)", + "name": "X-Auth", + "in": "header", + "required": true + }, { "description": "Update ZOS Version Request", "name": "body", @@ -957,6 +1061,45 @@ } } }, + "server.UpdateNodeRequest": { + "type": "object", + "required": [ + "farm_id", + "interfaces", + "location", + "resources", + "secure_boot", + "serial_number", + "virtualized" + ], + "properties": { + "farm_id": { + "type": "integer", + "minimum": 1 + }, + "interfaces": { + "type": "array", + "items": { + "$ref": "#/definitions/db.Interface" + } + }, + "location": { + "$ref": "#/definitions/db.Location" + }, + "resources": { + "$ref": "#/definitions/db.Resources" + }, + "secure_boot": { + "type": "boolean" + }, + "serial_number": { + "type": "string" + }, + "virtualized": { + "type": "boolean" + } + } + }, "server.UptimeReportRequest": { "type": "object", "required": [ @@ -986,14 +1129,6 @@ "time.Duration": { "type": "integer", "enum": [ - -9223372036854775808, - 9223372036854775807, - 1, - 1000, - 1000000, - 1000000000, - 60000000000, - 3600000000000, 1, 1000, 1000000, @@ -1001,14 +1136,6 @@ 60000000000 ], "x-enum-varnames": [ - "minDuration", - "maxDuration", - "Nanosecond", - "Microsecond", - "Millisecond", - "Second", - "Minute", - "Hour", "Nanosecond", "Microsecond", "Millisecond", diff --git a/node-registrar/docs/swagger.yaml b/node-registrar/docs/swagger.yaml index 1b133dd..96f9a40 100644 --- a/node-registrar/docs/swagger.yaml +++ b/node-registrar/docs/swagger.yaml @@ -1,3 +1,4 @@ +basePath: /v1 definitions: db.Account: properties: @@ -211,6 +212,34 @@ definitions: required: - farm_name type: object + server.UpdateNodeRequest: + properties: + farm_id: + minimum: 1 + type: integer + interfaces: + items: + $ref: '#/definitions/db.Interface' + type: array + location: + $ref: '#/definitions/db.Location' + resources: + $ref: '#/definitions/db.Resources' + secure_boot: + type: boolean + serial_number: + type: string + virtualized: + type: boolean + required: + - farm_id + - interfaces + - location + - resources + - secure_boot + - serial_number + - virtualized + type: object server.UptimeReportRequest: properties: timestamp: @@ -230,14 +259,6 @@ definitions: type: object time.Duration: enum: - - -9223372036854775808 - - 9223372036854775807 - - 1 - - 1000 - - 1000000 - - 1000000000 - - 60000000000 - - 3600000000000 - 1 - 1000 - 1000000 @@ -245,14 +266,6 @@ definitions: - 60000000000 type: integer x-enum-varnames: - - minDuration - - maxDuration - - Nanosecond - - Microsecond - - Millisecond - - Second - - Minute - - Hour - Nanosecond - Microsecond - Millisecond @@ -260,6 +273,9 @@ definitions: - Minute info: contact: {} + description: API for managing TFGrid node registration + title: Node Registrar API + version: "1.0" paths: /accounts: get: @@ -328,6 +344,11 @@ paths: - application/json description: Updates an account's relays and RMB encryption key parameters: + - description: 'Authentication format: Base64(:):Base64(signature)' + in: header + name: X-Auth + required: true + type: string - description: Twin ID of the account in: path name: twin_id @@ -404,6 +425,11 @@ paths: - application/json description: Create a new farm entry parameters: + - description: 'Authentication format: Base64(:):Base64(signature)' + in: header + name: X-Auth + required: true + type: string - description: Farm creation data in: body name: farm @@ -462,6 +488,11 @@ paths: - application/json description: Update existing farm details parameters: + - description: 'Authentication format: Base64(:):Base64(signature)' + in: header + name: X-Auth + required: true + type: string - description: Farm ID in: path name: farm_id @@ -546,6 +577,11 @@ paths: - application/json description: Register a new node in the system parameters: + - description: 'Authentication format: Base64(:):Base64(signature)' + in: header + name: X-Auth + required: true + type: string - description: Node registration data in: body name: request @@ -599,12 +635,56 @@ paths: summary: Get node details tags: - nodes + patch: + consumes: + - application/json + description: Update existing node details + parameters: + - description: 'Authentication format: Base64(:):Base64(signature)' + in: header + name: X-Auth + required: true + type: string + - description: Node ID + in: path + name: node_id + required: true + type: integer + - description: Node update data + in: body + name: request + required: true + schema: + $ref: '#/definitions/server.UpdateNodeRequest' + produces: + - application/json + responses: + "200": + description: Node updated successfully + schema: + $ref: '#/definitions/gin.H' + "400": + description: Invalid request + schema: + $ref: '#/definitions/gin.H' + "404": + description: Node not found + schema: + $ref: '#/definitions/gin.H' + summary: Update node + tags: + - nodes /nodes/{node_id}/uptime: post: consumes: - application/json description: Submit uptime report for a node parameters: + - description: 'Authentication format: Base64(:):Base64(signature)' + in: header + name: X-Auth + required: true + type: string - description: Node ID in: path name: node_id @@ -660,6 +740,11 @@ paths: - application/json description: Sets the ZOS version parameters: + - description: 'Authentication format: Base64(:):Base64(signature)' + in: header + name: X-Auth + required: true + type: string - description: Update ZOS Version Request in: body name: body diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index cef7f47..2260879 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -18,6 +18,11 @@ const ( MaxTimestampDelta = 2 * time.Second ) +// @title Node Registrar API +// @version 1.0 +// @description API for managing TFGrid node registration +// @BasePath /v1 + // @Summary List farms // @Description Get a list of farms with optional filters // @Tags farms @@ -90,6 +95,7 @@ func (s Server) getFarmHandler(c *gin.Context) { // @Summary Create new farm // @Description Create a new farm entry // @Tags farms +// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Accept json // @Produce json // @Param farm body db.Farm true "Farm creation data" @@ -135,6 +141,7 @@ type UpdateFarmRequest struct { // @Summary Update farm // @Description Update existing farm details // @Tags farms +// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Accept json // @Produce json // @Param farm_id path int true "Farm ID" @@ -279,6 +286,7 @@ type NodeRegistrationRequest struct { // @Summary Register new node // @Description Register a new node in the system // @Tags nodes +// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Accept json // @Produce json // @Param request body NodeRegistrationRequest true "Node registration data" @@ -339,6 +347,18 @@ type UpdateNodeRequest struct { SerialNumber string `json:"serial_number" binding:"required"` } +// @Summary Update node +// @Description Update existing node details +// @Tags nodes +// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" +// @Accept json +// @Produce json +// @Param node_id path int true "Node ID" +// @Param request body UpdateNodeRequest true "Node update data" +// @Success 200 {object} gin.H "Node updated successfully" +// @Failure 400 {object} gin.H "Invalid request" +// @Failure 404 {object} gin.H "Node not found" +// @Router /nodes/{node_id} [patch] func (s *Server) updateNodeHandler(c *gin.Context) { nodeID, err := strconv.ParseUint(c.Param("node_id"), 10, 64) if err != nil { @@ -401,6 +421,7 @@ type UptimeReportRequest struct { // @Summary Report node uptime // @Description Submit uptime report for a node // @Tags nodes +// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Accept json // @Produce json // @Param node_id path int true "Node ID" @@ -567,6 +588,7 @@ type UpdateAccountRequest struct { // @Summary Update account details // @Description Updates an account's relays and RMB encryption key // @Tags accounts +// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Accept json // @Produce json // @Param twin_id path uint64 true "Twin ID of the account" @@ -680,6 +702,7 @@ type ZOSVersionRequest struct { // @Summary Set ZOS Version // @Description Sets the ZOS version // @Tags ZOS +// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Accept json // @Produce json // @Param body body ZOSVersionRequest true "Update ZOS Version Request" From 950e57752f67c158d65c4f0ccec16f1a65f39338 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Wed, 29 Jan 2025 17:12:48 +0200 Subject: [PATCH 21/31] add 401 response swagger documentation --- node-registrar/pkg/server/handlers.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 2260879..33e8249 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -95,12 +95,13 @@ func (s Server) getFarmHandler(c *gin.Context) { // @Summary Create new farm // @Description Create a new farm entry // @Tags farms -// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Accept json // @Produce json +// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Param farm body db.Farm true "Farm creation data" // @Success 201 {object} gin.H "Farm created successfully" // @Failure 400 {object} gin.H "Invalid request" +// @Failure 401 {object} gin.H "Unauthorized" // @Failure 409 {object} gin.H "Farm already exists" // @Router /farms [post] func (s Server) createFarmHandler(c *gin.Context) { @@ -141,13 +142,14 @@ type UpdateFarmRequest struct { // @Summary Update farm // @Description Update existing farm details // @Tags farms -// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Accept json // @Produce json +// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Param farm_id path int true "Farm ID" // @Param request body UpdateFarmRequest true "Farm update data" // @Success 200 {object} gin.H "Farm updated successfully" // @Failure 400 {object} gin.H "Invalid request" +// @Failure 401 {object} gin.H "Unauthorized" // @Failure 404 {object} gin.H "Farm not found" // @Router /farms/{farm_id} [patch] func (s Server) updateFarmsHandler(c *gin.Context) { @@ -286,12 +288,13 @@ type NodeRegistrationRequest struct { // @Summary Register new node // @Description Register a new node in the system // @Tags nodes -// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Accept json // @Produce json +// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Param request body NodeRegistrationRequest true "Node registration data" // @Success 201 {object} gin.H "Node registered successfully" // @Failure 400 {object} gin.H "Invalid request" +// @Failure 401 {object} gin.H "Unauthorized" // @Failure 409 {object} gin.H "Node already exists" // @Router /nodes [post] func (s Server) registerNodeHandler(c *gin.Context) { @@ -350,13 +353,14 @@ type UpdateNodeRequest struct { // @Summary Update node // @Description Update existing node details // @Tags nodes -// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Accept json // @Produce json +// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Param node_id path int true "Node ID" // @Param request body UpdateNodeRequest true "Node update data" // @Success 200 {object} gin.H "Node updated successfully" // @Failure 400 {object} gin.H "Invalid request" +// @Failure 401 {object} gin.H "Unauthorized" // @Failure 404 {object} gin.H "Node not found" // @Router /nodes/{node_id} [patch] func (s *Server) updateNodeHandler(c *gin.Context) { @@ -421,13 +425,14 @@ type UptimeReportRequest struct { // @Summary Report node uptime // @Description Submit uptime report for a node // @Tags nodes -// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Accept json // @Produce json +// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Param node_id path int true "Node ID" // @Param request body UptimeReportRequest true "Uptime report data" // @Success 201 {object} gin.H "Uptime reported successfully" // @Failure 400 {object} gin.H "Invalid request" +// @Failure 401 {object} gin.H "Unauthorized" // @Failure 404 {object} gin.H "Node not found" // @Router /nodes/{node_id}/uptime [post] func (s *Server) uptimeReportHandler(c *gin.Context) { @@ -588,13 +593,14 @@ type UpdateAccountRequest struct { // @Summary Update account details // @Description Updates an account's relays and RMB encryption key // @Tags accounts -// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Accept json // @Produce json +// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Param twin_id path uint64 true "Twin ID of the account" // @Param account body UpdateAccountRequest true "Account details to update" // @Success 200 {object} gin.H "Account updated successfully" // @Failure 400 {object} gin.H "Invalid request" +// @Failure 401 {object} gin.H "Unauthorized" // @Failure 404 {object} gin.H "Account not found" // @Router /accounts/{twin_id} [patch] func (s *Server) updateAccountHandler(c *gin.Context) { @@ -702,12 +708,13 @@ type ZOSVersionRequest struct { // @Summary Set ZOS Version // @Description Sets the ZOS version // @Tags ZOS -// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Accept json // @Produce json +// @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Param body body ZOSVersionRequest true "Update ZOS Version Request" // @Success 200 {object} gin.H "OK" // @Failure 400 {object} gin.H "Bad Request" +// @Failure 401 {object} gin.H "Unauthorized" // @Failure 409 {object} gin.H "Conflict" // @Failure 500 {object} gin.H "Internal Server Error" // @Router /zos/version [post] From 9569400a696bec76b37d1784b7437945f4aebf16 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Wed, 29 Jan 2025 18:10:47 +0200 Subject: [PATCH 22/31] Uses GORM's serializer instead of using custom Scan/Value implementation --- node-registrar/pkg/db/models.go | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index 163211d..a5336b2 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -1,9 +1,6 @@ package db import ( - "database/sql/driver" - "encoding/json" - "fmt" "time" "github.com/lib/pq" @@ -40,11 +37,11 @@ type Node struct { FarmID uint64 `json:"farm_id" gorm:"not null;check:farm_id> 0;foreignKey:FarmID;references:FarmID;constraint:OnDelete:RESTRICT"` TwinID uint64 `json:"twin_id" gorm:"not null;unique;check:twin_id > 0;foreignKey:TwinID;references:TwinID;constraint:OnDelete:RESTRICT"` // Node account reference - Location Location `json:"location" gorm:"not null;type:json"` + Location Location `json:"location" gorm:"not null;type:json;serializer:json"` // PublicConfig PublicConfig `json:"public_config" gorm:"type:json"` - Resources Resources `json:"resources" gorm:"not null;type:json"` - Interfaces []Interface `json:"interface" gorm:"not null;type:json"` + Resources Resources `json:"resources" gorm:"not null;type:json;serializer:json"` + Interfaces []Interface `json:"interface" gorm:"not null;type:json;serializer:json"` SecureBoot bool Virtualized bool SerialNumber string @@ -76,7 +73,7 @@ type Interface struct { IPs string `json:"ips"` } -// Value implements the Valuer interface for storing Interface in the database +/* // Value implements the Valuer interface for storing Interface in the database func (i Interface) Value() (driver.Value, error) { bytes, err := json.Marshal(i) if err != nil { @@ -96,7 +93,7 @@ func (i *Interface) Scan(value any) error { return fmt.Errorf("failed to unmarshal Interface: %w", err) } return nil -} +} */ type Resources struct { HRU uint64 `json:"hru"` @@ -105,7 +102,7 @@ type Resources struct { MRU uint64 `json:"mru"` } -// Value implements the Valuer interface for storing Resources in the database +/* // Value implements the Valuer interface for storing Resources in the database func (r Resources) Value() (driver.Value, error) { bytes, err := json.Marshal(r) if err != nil { @@ -125,7 +122,7 @@ func (r *Resources) Scan(value any) error { return fmt.Errorf("failed to unmarshal resources: %w", err) } return nil -} +} */ type Location struct { Country string `json:"country" gorm:"not null"` @@ -134,7 +131,7 @@ type Location struct { Latitude string `json:"latitude" gorm:"not null"` } -// Value implements the Valuer interface for storing Location in the database +/* // Value implements the Valuer interface for storing Location in the database func (l Location) Value() (driver.Value, error) { bytes, err := json.Marshal(l) if err != nil { @@ -154,7 +151,7 @@ func (l *Location) Scan(value any) error { return fmt.Errorf("failed to unmarshal Location: %w", err) } return nil -} +} */ // type PublicConfig struct { // PublicIPV4 string `json:"public_ip_v4"` From 9e8cb98d03cb1efb933b66df267e9e51e7a1eec2 Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Mon, 3 Feb 2025 16:43:09 +0200 Subject: [PATCH 23/31] update json names in node registrar --- node-registrar/pkg/db/models.go | 6 +++--- node-registrar/pkg/server/handlers.go | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index a5336b2..9985081 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -7,14 +7,14 @@ import ( ) type Account struct { - TwinID uint64 `gorm:"primaryKey;autoIncrement"` + TwinID uint64 `gorm:"primaryKey;autoIncrement" json:"twin_id"` Relays pq.StringArray `gorm:"type:text[];default:'{}'" json:"relays"` // Optional list of relay domains RMBEncKey string `gorm:"type:text" json:"rmb_enc_key"` // Optional base64 encoded public key for rmb communication CreatedAt time.Time UpdatedAt time.Time // The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substrate echo system? // (still SS58 can be used or plain base58 ,TBD) - PublicKey string `gorm:"type:text;not null;unique"` + PublicKey string `gorm:"type:text;not null;unique" json:"public_key"` // Relations | likely we need to use OnDelete:RESTRICT (Prevent Twin deletion if farms exist) // @swagger:ignore Farms []Farm `gorm:"foreignKey:TwinID;references:TwinID;constraint:OnDelete:RESTRICT"` @@ -55,7 +55,7 @@ type Node struct { type UptimeReport struct { ID uint64 `gorm:"primaryKey;autoIncrement"` - NodeID uint64 `gorm:"index"` + NodeID uint64 `gorm:"index" json:"node_id"` Duration time.Duration // Uptime duration for this period Timestamp time.Time `gorm:"index"` WasRestart bool // True if this report followed a restart diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 33e8249..2cc0277 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -10,6 +10,7 @@ import ( "github.com/gin-gonic/gin" "github.com/lib/pq" + "github.com/rs/zerolog/log" "github.com/threefoldtech/tfgrid-sdk-go/node-registrar/pkg/db" ) @@ -385,6 +386,7 @@ func (s *Server) updateNodeHandler(c *gin.Context) { return } + log.Info().Any("req is", c.Request.Body) var req UpdateNodeRequest if err := c.ShouldBindJSON(&req); err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid request body"}) @@ -696,7 +698,8 @@ func (s *Server) getAccountHandler(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get account"}) return } - c.JSON(http.StatusOK, account) + log.Info().Any("account", account).Send() + c.JSON(http.StatusOK, gin.H{"account": account}) return } } From cbeaa01a22270228ce653617c675949e5e749070 Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Tue, 4 Feb 2025 13:50:56 +0200 Subject: [PATCH 24/31] return account from get accout --- node-registrar/pkg/server/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 2cc0277..940538c 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -699,7 +699,7 @@ func (s *Server) getAccountHandler(c *gin.Context) { return } log.Info().Any("account", account).Send() - c.JSON(http.StatusOK, gin.H{"account": account}) + c.JSON(http.StatusOK, account) return } } From 357ff8e42ba98ed1366453a2c4bbe67cef54a185 Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Tue, 4 Feb 2025 15:33:11 +0200 Subject: [PATCH 25/31] remove print filter --- node-registrar/pkg/server/handlers.go | 1 - 1 file changed, 1 deletion(-) diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 940538c..d7ad6f1 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -227,7 +227,6 @@ func (s Server) listNodesHandler(c *gin.Context) { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - fmt.Println(filter, limit) nodes, err := s.db.ListNodes(filter, limit) if err != nil { From 9f8dff658bfa9a35f37b6d7636190366c16e8a3e Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Tue, 4 Feb 2025 17:48:34 +0200 Subject: [PATCH 26/31] update updateNodeRequest rules --- node-registrar/pkg/server/handlers.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index d7ad6f1..ebed5a2 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -342,11 +342,11 @@ func (s Server) registerNodeHandler(c *gin.Context) { type UpdateNodeRequest struct { FarmID uint64 `json:"farm_id" binding:"required,min=1"` - Resources db.Resources `json:"resources" binding:"required,min=1"` + Resources db.Resources `json:"resources" binding:"required"` Location db.Location `json:"location" binding:"required"` - Interfaces []db.Interface `json:"interfaces" binding:"required,dive"` - SecureBoot bool `json:"secure_boot" binding:"required"` - Virtualized bool `json:"virtualized" binding:"required"` + Interfaces []db.Interface `json:"interfaces" binding:"required"` + SecureBoot bool `json:"secure_boot"` + Virtualized bool `json:"virtualized"` SerialNumber string `json:"serial_number" binding:"required"` } @@ -391,6 +391,7 @@ func (s *Server) updateNodeHandler(c *gin.Context) { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid request body"}) return } + log.Debug().Any("req", req).Send() // Prepare update fields updates := map[string]interface{}{ From 1be4c38953cfe75f6322c75bba9044356f0ce861 Mon Sep 17 00:00:00 2001 From: Omar Abdulaziz Date: Wed, 5 Feb 2025 02:09:44 +0200 Subject: [PATCH 27/31] use Node struct to update instead on map - use the node struct to update, to utilize the json serilaizer for array types like []interface - remove unneeded constrains from the NodeUpdateRequest struct --- node-registrar/pkg/db/models.go | 2 +- node-registrar/pkg/db/nodes.go | 4 ++-- node-registrar/pkg/server/handlers.go | 24 ++++++++++++------------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index 9985081..05202e0 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -41,7 +41,7 @@ type Node struct { // PublicConfig PublicConfig `json:"public_config" gorm:"type:json"` Resources Resources `json:"resources" gorm:"not null;type:json;serializer:json"` - Interfaces []Interface `json:"interface" gorm:"not null;type:json;serializer:json"` + Interfaces []Interface `gorm:"not null;type:json;serializer:json"` SecureBoot bool Virtualized bool SerialNumber string diff --git a/node-registrar/pkg/db/nodes.go b/node-registrar/pkg/db/nodes.go index 0d2130e..0d01f4b 100644 --- a/node-registrar/pkg/db/nodes.go +++ b/node-registrar/pkg/db/nodes.go @@ -53,10 +53,10 @@ func (db *Database) RegisterNode(node Node) (uint64, error) { return node.NodeID, nil } -func (db *Database) UpdateNode(nodeID uint64, updates map[string]interface{}) error { +func (db *Database) UpdateNode(nodeID uint64, node Node) error { result := db.gormDB.Model(&Node{}). Where("node_id = ?", nodeID). - Updates(updates) + Updates(node) if result.Error != nil { return result.Error diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index ebed5a2..c30c483 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -279,7 +279,7 @@ type NodeRegistrationRequest struct { FarmID uint64 `json:"farm_id" binding:"required,min=1"` Resources db.Resources `json:"resources" binding:"required"` Location db.Location `json:"location" binding:"required"` - Interfaces []db.Interface `json:"interfaces" binding:"required,min=1,dive"` + Interfaces []db.Interface `json:"interfaces" binding:"required"` SecureBoot bool `json:"secure_boot"` Virtualized bool `json:"virtualized"` SerialNumber string `json:"serial_number" binding:"required"` @@ -393,21 +393,21 @@ func (s *Server) updateNodeHandler(c *gin.Context) { } log.Debug().Any("req", req).Send() - // Prepare update fields - updates := map[string]interface{}{ - "farm_id": req.FarmID, - "resources": req.Resources, - "location": req.Location, - "interfaces": req.Interfaces, - "secure_boot": req.SecureBoot, - "virtualized": req.Virtualized, - "serial_number": req.SerialNumber, + updatedNode := db.Node{ + FarmID: req.FarmID, + Resources: req.Resources, + Location: req.Location, + Interfaces: req.Interfaces, + SecureBoot: req.SecureBoot, + Virtualized: req.Virtualized, + SerialNumber: req.SerialNumber, } + if req.FarmID != existingNode.FarmID { - updates["approved"] = false + updatedNode.Approved = false } - if err := s.db.UpdateNode(nodeID, updates); err != nil { + if err := s.db.UpdateNode(nodeID, updatedNode); err != nil { if errors.Is(err, db.ErrRecordNotFound) { c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": "node not found"}) return From 261a264b26e83f8f537e589e5cf30cb88a8c9314 Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Wed, 5 Feb 2025 10:31:07 +0200 Subject: [PATCH 28/31] fix inconsistencies in server returns --- node-registrar/pkg/server/handlers.go | 120 ++++++++++++-------------- 1 file changed, 53 insertions(+), 67 deletions(-) diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index c30c483..6aca69e 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -34,8 +34,8 @@ const ( // @Param twin_id query int false "Filter by twin ID" // @Param page query int false "Page number" default(1) // @Param size query int false "Results per page" default(10) -// @Success 200 {object} gin.H "List of farms" -// @Failure 400 {object} gin.H "Bad request" +// @Success 200 {object} []db.Farm "List of farms" +// @Failure 400 {object} map[string]any "Bad request" // @Router /farms [get] func (s Server) listFarmsHandler(c *gin.Context) { var filter db.FarmFilter @@ -53,9 +53,7 @@ func (s Server) listFarmsHandler(c *gin.Context) { return } - c.JSON(http.StatusOK, gin.H{ - "farms": farms, - }) + c.JSON(http.StatusOK, farms) } // @Summary Get farm details @@ -64,9 +62,9 @@ func (s Server) listFarmsHandler(c *gin.Context) { // @Accept json // @Produce json // @Param farm_id path int true "Farm ID" -// @Success 200 {object} gin.H "Farm details" -// @Failure 400 {object} gin.H "Invalid farm ID" -// @Failure 404 {object} gin.H "Farm not found" +// @Success 200 {object} db.Farm "Farm details" +// @Failure 400 {object} map[string]any "Invalid farm ID" +// @Failure 404 {object} map[string]any "Farm not found" // @Router /farms/{farm_id} [get] func (s Server) getFarmHandler(c *gin.Context) { farmID := c.Param("farm_id") @@ -88,9 +86,7 @@ func (s Server) getFarmHandler(c *gin.Context) { c.JSON(status, gin.H{"error": err.Error()}) } - c.JSON(http.StatusOK, gin.H{ - "farm": farm, - }) + c.JSON(http.StatusOK, farm) } // @Summary Create new farm @@ -100,10 +96,10 @@ func (s Server) getFarmHandler(c *gin.Context) { // @Produce json // @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Param farm body db.Farm true "Farm creation data" -// @Success 201 {object} gin.H "Farm created successfully" -// @Failure 400 {object} gin.H "Invalid request" -// @Failure 401 {object} gin.H "Unauthorized" -// @Failure 409 {object} gin.H "Farm already exists" +// @Success 201 {object} db.Farm "Farm created successfully" +// @Failure 400 {object} map[string]any "Invalid request" +// @Failure 401 {object} map[string]any "Unauthorized" +// @Failure 409 {object} map[string]any "Farm already exists" // @Router /farms [post] func (s Server) createFarmHandler(c *gin.Context) { var farm db.Farm @@ -130,10 +126,7 @@ func (s Server) createFarmHandler(c *gin.Context) { return } - c.JSON(http.StatusCreated, gin.H{ - "message": "Farm created successfully", - "farm_id": farmID, - }) + c.JSON(http.StatusCreated, farmID) } type UpdateFarmRequest struct { @@ -148,10 +141,10 @@ type UpdateFarmRequest struct { // @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Param farm_id path int true "Farm ID" // @Param request body UpdateFarmRequest true "Farm update data" -// @Success 200 {object} gin.H "Farm updated successfully" -// @Failure 400 {object} gin.H "Invalid request" -// @Failure 401 {object} gin.H "Unauthorized" -// @Failure 404 {object} gin.H "Farm not found" +// @Success 200 {object} map[string]any "Farm updated successfully" +// @Failure 400 {object} map[string]any "Invalid request" +// @Failure 401 {object} map[string]any "Unauthorized" +// @Failure 404 {object} map[string]any "Farm not found" // @Router /farms/{farm_id} [patch] func (s Server) updateFarmsHandler(c *gin.Context) { var req UpdateFarmRequest @@ -215,8 +208,8 @@ func (s Server) updateFarmsHandler(c *gin.Context) { // @Param healthy query bool false "Filter by health status" // @Param page query int false "Page number" default(1) // @Param size query int false "Results per page" default(10) -// @Success 200 {object} gin.H "List of nodes" -// @Failure 400 {object} gin.H "Bad request" +// @Success 200 {object} []db.Node "List of nodes" +// @Failure 400 {object} map[string]any "Bad request" // @Router /nodes [get] func (s Server) listNodesHandler(c *gin.Context) { var filter db.NodeFilter @@ -234,9 +227,7 @@ func (s Server) listNodesHandler(c *gin.Context) { return } - c.JSON(http.StatusOK, gin.H{ - "nodes": nodes, - }) + c.JSON(http.StatusOK, nodes) } // @Summary Get node details @@ -245,9 +236,9 @@ func (s Server) listNodesHandler(c *gin.Context) { // @Accept json // @Produce json // @Param node_id path int true "Node ID" -// @Success 200 {object} gin.H "Node details" -// @Failure 400 {object} gin.H "Invalid node ID" -// @Failure 404 {object} gin.H "Node not found" +// @Success 200 {object} db.Node "Node details" +// @Failure 400 {object} map[string]any "Invalid node ID" +// @Failure 404 {object} map[string]any "Node not found" // @Router /nodes/{node_id} [get] func (s Server) getNodeHandler(c *gin.Context) { nodeID := c.Param("node_id") @@ -269,9 +260,7 @@ func (s Server) getNodeHandler(c *gin.Context) { return } - c.JSON(http.StatusOK, gin.H{ - "node": node, - }) + c.JSON(http.StatusOK, node) } type NodeRegistrationRequest struct { @@ -292,10 +281,10 @@ type NodeRegistrationRequest struct { // @Produce json // @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Param request body NodeRegistrationRequest true "Node registration data" -// @Success 201 {object} gin.H "Node registered successfully" -// @Failure 400 {object} gin.H "Invalid request" -// @Failure 401 {object} gin.H "Unauthorized" -// @Failure 409 {object} gin.H "Node already exists" +// @Success 201 {object} uint64 "ID of the created node" +// @Failure 400 {object} map[string]any "Invalid request" +// @Failure 401 {object} map[string]any "Unauthorized" +// @Failure 409 {object} map[string]any "Node already exists" // @Router /nodes [post] func (s Server) registerNodeHandler(c *gin.Context) { var req NodeRegistrationRequest @@ -334,10 +323,7 @@ func (s Server) registerNodeHandler(c *gin.Context) { return } - c.JSON(http.StatusCreated, gin.H{ - "message": "node registered successfully", - "node_id": nodeID, - }) + c.JSON(http.StatusCreated, nodeID) } type UpdateNodeRequest struct { @@ -358,10 +344,10 @@ type UpdateNodeRequest struct { // @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Param node_id path int true "Node ID" // @Param request body UpdateNodeRequest true "Node update data" -// @Success 200 {object} gin.H "Node updated successfully" -// @Failure 400 {object} gin.H "Invalid request" -// @Failure 401 {object} gin.H "Unauthorized" -// @Failure 404 {object} gin.H "Node not found" +// @Success 200 {object} map[string]any "Node updated successfully" +// @Failure 400 {object} map[string]any "Invalid request" +// @Failure 401 {object} map[string]any "Unauthorized" +// @Failure 404 {object} map[string]any "Node not found" // @Router /nodes/{node_id} [patch] func (s *Server) updateNodeHandler(c *gin.Context) { nodeID, err := strconv.ParseUint(c.Param("node_id"), 10, 64) @@ -432,10 +418,10 @@ type UptimeReportRequest struct { // @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Param node_id path int true "Node ID" // @Param request body UptimeReportRequest true "Uptime report data" -// @Success 201 {object} gin.H "Uptime reported successfully" -// @Failure 400 {object} gin.H "Invalid request" -// @Failure 401 {object} gin.H "Unauthorized" -// @Failure 404 {object} gin.H "Node not found" +// @Success 201 {object} map[string]any "Uptime reported successfully" +// @Failure 400 {object} map[string]any "Invalid request" +// @Failure 401 {object} map[string]any "Unauthorized" +// @Failure 404 {object} map[string]any "Node not found" // @Router /nodes/{node_id}/uptime [post] func (s *Server) uptimeReportHandler(c *gin.Context) { nodeID := c.Param("node_id") @@ -511,8 +497,8 @@ type AccountCreationRequest struct { // @Produce json // @Param request body AccountCreationRequest true "Account creation data" // @Success 201 {object} db.Account "Created account details" -// @Failure 400 {object} gin.H "Invalid request" -// @Failure 409 {object} gin.H "Account already exists" +// @Failure 400 {object} map[string]any "Invalid request" +// @Failure 409 {object} map[string]any "Account already exists" // @Router /accounts [post] func (s *Server) createAccountHandler(c *gin.Context) { var req AccountCreationRequest @@ -600,10 +586,10 @@ type UpdateAccountRequest struct { // @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Param twin_id path uint64 true "Twin ID of the account" // @Param account body UpdateAccountRequest true "Account details to update" -// @Success 200 {object} gin.H "Account updated successfully" -// @Failure 400 {object} gin.H "Invalid request" -// @Failure 401 {object} gin.H "Unauthorized" -// @Failure 404 {object} gin.H "Account not found" +// @Success 200 {object} map[string]any "Account updated successfully" +// @Failure 400 {object} map[string]any "Invalid request" +// @Failure 401 {object} map[string]any "Unauthorized" +// @Failure 404 {object} map[string]any "Account not found" // @Router /accounts/{twin_id} [patch] func (s *Server) updateAccountHandler(c *gin.Context) { twinID, err := strconv.ParseUint(c.Param("twin_id"), 10, 64) @@ -645,8 +631,8 @@ func (s *Server) updateAccountHandler(c *gin.Context) { // @Param twin_id query uint64 false "Twin ID of the account" // @Param public_key query string false "Base64 decoded Public key of the account" // @Success 200 {object} db.Account "Account details" -// @Failure 400 {object} gin.H "Invalid request" -// @Failure 404 {object} gin.H "Account not found" +// @Failure 400 {object} map[string]any "Invalid request" +// @Failure 404 {object} map[string]any "Account not found" // @Router /accounts [get] func (s *Server) getAccountHandler(c *gin.Context) { twinIDParam := c.Query("twin_id") @@ -715,11 +701,11 @@ type ZOSVersionRequest struct { // @Produce json // @Param X-Auth header string true "Authentication format: Base64(:):Base64(signature)" // @Param body body ZOSVersionRequest true "Update ZOS Version Request" -// @Success 200 {object} gin.H "OK" -// @Failure 400 {object} gin.H "Bad Request" -// @Failure 401 {object} gin.H "Unauthorized" -// @Failure 409 {object} gin.H "Conflict" -// @Failure 500 {object} gin.H "Internal Server Error" +// @Success 200 {object} map[string]any "OK" +// @Failure 400 {object} map[string]any "Bad Request" +// @Failure 401 {object} map[string]any "Unauthorized" +// @Failure 409 {object} map[string]any "Conflict" +// @Failure 500 {object} map[string]any "Internal Server Error" // @Router /zos/version [post] func (s *Server) setZOSVersionHandler(c *gin.Context) { ensureOwner(c, s.adminTwinID) @@ -749,9 +735,9 @@ func (s *Server) setZOSVersionHandler(c *gin.Context) { // @Description Gets the ZOS version // @Tags ZOS // @Produce json -// @Success 200 {object} gin.H "OK" -// @Failure 404 {object} gin.H "Not Found" -// @Failure 500 {object} gin.H "Internal Server Error" +// @Success 200 {object} string "zos version" +// @Failure 404 {object} map[string]any "Not Found" +// @Failure 500 {object} map[string]any "Internal Server Error" // @Router /zos/version [get] func (s *Server) getZOSVersionHandler(c *gin.Context) { version, err := s.db.GetZOSVersion() @@ -764,7 +750,7 @@ func (s *Server) getZOSVersionHandler(c *gin.Context) { return } - c.JSON(http.StatusOK, gin.H{"version": version}) + c.JSON(http.StatusOK, version) } // Helper function to validate public key format From 23b3946a908706b2bda727d8529a0a215a78cb9b Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Wed, 5 Feb 2025 10:31:22 +0200 Subject: [PATCH 29/31] update swagger docs --- node-registrar/docs/docs.go | 244 ++++++++++++++++++------------- node-registrar/docs/swagger.json | 242 +++++++++++++++++------------- node-registrar/docs/swagger.yaml | 203 ++++++++++++++----------- node-registrar/pkg/db/models.go | 6 +- 4 files changed, 397 insertions(+), 298 deletions(-) diff --git a/node-registrar/docs/docs.go b/node-registrar/docs/docs.go index dd4519d..d76e23c 100644 --- a/node-registrar/docs/docs.go +++ b/node-registrar/docs/docs.go @@ -52,13 +52,15 @@ const docTemplate = `{ "400": { "description": "Invalid request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "404": { "description": "Account not found", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -96,13 +98,15 @@ const docTemplate = `{ "400": { "description": "Invalid request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "409": { "description": "Account already exists", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -150,19 +154,29 @@ const docTemplate = `{ "200": { "description": "Account updated successfully", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "400": { "description": "Invalid request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true } }, "404": { "description": "Account not found", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -219,13 +233,17 @@ const docTemplate = `{ "200": { "description": "List of farms", "schema": { - "$ref": "#/definitions/gin.H" + "type": "array", + "items": { + "$ref": "#/definitions/db.Farm" + } } }, "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -264,19 +282,28 @@ const docTemplate = `{ "201": { "description": "Farm created successfully", "schema": { - "$ref": "#/definitions/gin.H" + "$ref": "#/definitions/db.Farm" } }, "400": { "description": "Invalid request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true } }, "409": { "description": "Farm already exists", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -308,19 +335,21 @@ const docTemplate = `{ "200": { "description": "Farm details", "schema": { - "$ref": "#/definitions/gin.H" + "$ref": "#/definitions/db.Farm" } }, "400": { "description": "Invalid farm ID", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "404": { "description": "Farm not found", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -366,19 +395,29 @@ const docTemplate = `{ "200": { "description": "Farm updated successfully", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "400": { "description": "Invalid request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true } }, "404": { "description": "Farm not found", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -447,13 +486,17 @@ const docTemplate = `{ "200": { "description": "List of nodes", "schema": { - "$ref": "#/definitions/gin.H" + "type": "array", + "items": { + "$ref": "#/definitions/db.Node" + } } }, "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -490,21 +533,30 @@ const docTemplate = `{ ], "responses": { "201": { - "description": "Node registered successfully", + "description": "ID of the created node", "schema": { - "$ref": "#/definitions/gin.H" + "type": "integer" } }, "400": { "description": "Invalid request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true } }, "409": { "description": "Node already exists", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -536,19 +588,21 @@ const docTemplate = `{ "200": { "description": "Node details", "schema": { - "$ref": "#/definitions/gin.H" + "$ref": "#/definitions/db.Node" } }, "400": { "description": "Invalid node ID", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "404": { "description": "Node not found", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -594,19 +648,29 @@ const docTemplate = `{ "200": { "description": "Node updated successfully", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "400": { "description": "Invalid request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true } }, "404": { "description": "Node not found", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -654,19 +718,29 @@ const docTemplate = `{ "201": { "description": "Uptime reported successfully", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "400": { "description": "Invalid request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true } }, "404": { "description": "Node not found", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -684,21 +758,23 @@ const docTemplate = `{ "summary": "Get ZOS Version", "responses": { "200": { - "description": "OK", + "description": "zos version", "schema": { - "$ref": "#/definitions/gin.H" + "type": "string" } }, "404": { "description": "Not Found", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -737,25 +813,36 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true } }, "409": { "description": "Conflict", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -776,7 +863,7 @@ const docTemplate = `{ "$ref": "#/definitions/db.Farm" } }, - "publicKey": { + "public_key": { "description": "The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substrate echo system?\n(still SS58 can be used or plain base58 ,TBD)", "type": "string" }, @@ -791,7 +878,7 @@ const docTemplate = `{ "description": "Optional base64 encoded public key for rmb communication", "type": "string" }, - "twinID": { + "twin_id": { "type": "integer" }, "updatedAt": { @@ -874,7 +961,7 @@ const docTemplate = `{ "description": "Constraints set to prevents unintended account deletion if linked Farms/nodes exist.", "type": "integer" }, - "interface": { + "interfaces": { "type": "array", "items": { "$ref": "#/definitions/db.Interface" @@ -943,16 +1030,12 @@ const docTemplate = `{ }, "duration": { "description": "Uptime duration for this period", - "allOf": [ - { - "$ref": "#/definitions/time.Duration" - } - ] + "type": "integer" }, "id": { "type": "integer" }, - "nodeID": { + "node_id": { "type": "integer" }, "timestamp": { @@ -964,10 +1047,6 @@ const docTemplate = `{ } } }, - "gin.H": { - "type": "object", - "additionalProperties": {} - }, "server.AccountCreationRequest": { "type": "object", "required": [ @@ -1015,7 +1094,6 @@ const docTemplate = `{ }, "interfaces": { "type": "array", - "minItems": 1, "items": { "$ref": "#/definitions/db.Interface" } @@ -1042,18 +1120,7 @@ const docTemplate = `{ } }, "server.UpdateAccountRequest": { - "type": "object", - "properties": { - "relays": { - "type": "array", - "items": { - "type": "string" - } - }, - "rmb_enc_key": { - "type": "string" - } - } + "type": "object" }, "server.UpdateFarmRequest": { "type": "object", @@ -1075,9 +1142,7 @@ const docTemplate = `{ "interfaces", "location", "resources", - "secure_boot", - "serial_number", - "virtualized" + "serial_number" ], "properties": { "farm_id": { @@ -1108,19 +1173,7 @@ const docTemplate = `{ } }, "server.UptimeReportRequest": { - "type": "object", - "required": [ - "timestamp", - "uptime" - ], - "properties": { - "timestamp": { - "type": "string" - }, - "uptime": { - "$ref": "#/definitions/time.Duration" - } - } + "type": "object" }, "server.ZOSVersionRequest": { "type": "object", @@ -1132,35 +1185,18 @@ const docTemplate = `{ "type": "string" } } - }, - "time.Duration": { - "type": "integer", - "enum": [ - 1, - 1000, - 1000000, - 1000000000, - 60000000000 - ], - "x-enum-varnames": [ - "Nanosecond", - "Microsecond", - "Millisecond", - "Second", - "Minute" - ] } } }` // SwaggerInfo holds exported Swagger Info so clients can modify it var SwaggerInfo = &swag.Spec{ - Version: "1.0", + Version: "", Host: "", - BasePath: "/v1", + BasePath: "", Schemes: []string{}, - Title: "Node Registrar API", - Description: "API for managing TFGrid node registration", + Title: "", + Description: "", InfoInstanceName: "swagger", SwaggerTemplate: docTemplate, LeftDelim: "{{", diff --git a/node-registrar/docs/swagger.json b/node-registrar/docs/swagger.json index d76bdf7..bd8e35f 100644 --- a/node-registrar/docs/swagger.json +++ b/node-registrar/docs/swagger.json @@ -1,12 +1,8 @@ { "swagger": "2.0", "info": { - "description": "API for managing TFGrid node registration", - "title": "Node Registrar API", - "contact": {}, - "version": "1.0" + "contact": {} }, - "basePath": "/v1", "paths": { "/accounts": { "get": { @@ -45,13 +41,15 @@ "400": { "description": "Invalid request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "404": { "description": "Account not found", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -89,13 +87,15 @@ "400": { "description": "Invalid request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "409": { "description": "Account already exists", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -143,19 +143,29 @@ "200": { "description": "Account updated successfully", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "400": { "description": "Invalid request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true } }, "404": { "description": "Account not found", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -212,13 +222,17 @@ "200": { "description": "List of farms", "schema": { - "$ref": "#/definitions/gin.H" + "type": "array", + "items": { + "$ref": "#/definitions/db.Farm" + } } }, "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -257,19 +271,28 @@ "201": { "description": "Farm created successfully", "schema": { - "$ref": "#/definitions/gin.H" + "$ref": "#/definitions/db.Farm" } }, "400": { "description": "Invalid request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true } }, "409": { "description": "Farm already exists", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -301,19 +324,21 @@ "200": { "description": "Farm details", "schema": { - "$ref": "#/definitions/gin.H" + "$ref": "#/definitions/db.Farm" } }, "400": { "description": "Invalid farm ID", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "404": { "description": "Farm not found", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -359,19 +384,29 @@ "200": { "description": "Farm updated successfully", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "400": { "description": "Invalid request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true } }, "404": { "description": "Farm not found", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -440,13 +475,17 @@ "200": { "description": "List of nodes", "schema": { - "$ref": "#/definitions/gin.H" + "type": "array", + "items": { + "$ref": "#/definitions/db.Node" + } } }, "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -483,21 +522,30 @@ ], "responses": { "201": { - "description": "Node registered successfully", + "description": "ID of the created node", "schema": { - "$ref": "#/definitions/gin.H" + "type": "integer" } }, "400": { "description": "Invalid request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true } }, "409": { "description": "Node already exists", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -529,19 +577,21 @@ "200": { "description": "Node details", "schema": { - "$ref": "#/definitions/gin.H" + "$ref": "#/definitions/db.Node" } }, "400": { "description": "Invalid node ID", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "404": { "description": "Node not found", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -587,19 +637,29 @@ "200": { "description": "Node updated successfully", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "400": { "description": "Invalid request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true } }, "404": { "description": "Node not found", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -647,19 +707,29 @@ "201": { "description": "Uptime reported successfully", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "400": { "description": "Invalid request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true } }, "404": { "description": "Node not found", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -677,21 +747,23 @@ "summary": "Get ZOS Version", "responses": { "200": { - "description": "OK", + "description": "zos version", "schema": { - "$ref": "#/definitions/gin.H" + "type": "string" } }, "404": { "description": "Not Found", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -730,25 +802,36 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true } }, "409": { "description": "Conflict", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/gin.H" + "type": "object", + "additionalProperties": true } } } @@ -769,7 +852,7 @@ "$ref": "#/definitions/db.Farm" } }, - "publicKey": { + "public_key": { "description": "The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substrate echo system?\n(still SS58 can be used or plain base58 ,TBD)", "type": "string" }, @@ -784,7 +867,7 @@ "description": "Optional base64 encoded public key for rmb communication", "type": "string" }, - "twinID": { + "twin_id": { "type": "integer" }, "updatedAt": { @@ -867,7 +950,7 @@ "description": "Constraints set to prevents unintended account deletion if linked Farms/nodes exist.", "type": "integer" }, - "interface": { + "interfaces": { "type": "array", "items": { "$ref": "#/definitions/db.Interface" @@ -936,16 +1019,12 @@ }, "duration": { "description": "Uptime duration for this period", - "allOf": [ - { - "$ref": "#/definitions/time.Duration" - } - ] + "type": "integer" }, "id": { "type": "integer" }, - "nodeID": { + "node_id": { "type": "integer" }, "timestamp": { @@ -957,10 +1036,6 @@ } } }, - "gin.H": { - "type": "object", - "additionalProperties": {} - }, "server.AccountCreationRequest": { "type": "object", "required": [ @@ -1008,7 +1083,6 @@ }, "interfaces": { "type": "array", - "minItems": 1, "items": { "$ref": "#/definitions/db.Interface" } @@ -1035,18 +1109,7 @@ } }, "server.UpdateAccountRequest": { - "type": "object", - "properties": { - "relays": { - "type": "array", - "items": { - "type": "string" - } - }, - "rmb_enc_key": { - "type": "string" - } - } + "type": "object" }, "server.UpdateFarmRequest": { "type": "object", @@ -1068,9 +1131,7 @@ "interfaces", "location", "resources", - "secure_boot", - "serial_number", - "virtualized" + "serial_number" ], "properties": { "farm_id": { @@ -1101,19 +1162,7 @@ } }, "server.UptimeReportRequest": { - "type": "object", - "required": [ - "timestamp", - "uptime" - ], - "properties": { - "timestamp": { - "type": "string" - }, - "uptime": { - "$ref": "#/definitions/time.Duration" - } - } + "type": "object" }, "server.ZOSVersionRequest": { "type": "object", @@ -1125,23 +1174,6 @@ "type": "string" } } - }, - "time.Duration": { - "type": "integer", - "enum": [ - 1, - 1000, - 1000000, - 1000000000, - 60000000000 - ], - "x-enum-varnames": [ - "Nanosecond", - "Microsecond", - "Millisecond", - "Second", - "Minute" - ] } } } \ No newline at end of file diff --git a/node-registrar/docs/swagger.yaml b/node-registrar/docs/swagger.yaml index 96f9a40..f28575f 100644 --- a/node-registrar/docs/swagger.yaml +++ b/node-registrar/docs/swagger.yaml @@ -1,4 +1,3 @@ -basePath: /v1 definitions: db.Account: properties: @@ -11,7 +10,7 @@ definitions: items: $ref: '#/definitions/db.Farm' type: array - publicKey: + public_key: description: |- The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substrate echo system? (still SS58 can be used or plain base58 ,TBD) @@ -24,7 +23,7 @@ definitions: rmb_enc_key: description: Optional base64 encoded public key for rmb communication type: string - twinID: + twin_id: type: integer updatedAt: type: string @@ -80,7 +79,7 @@ definitions: description: Constraints set to prevents unintended account deletion if linked Farms/nodes exist. type: integer - interface: + interfaces: items: $ref: '#/definitions/db.Interface' type: array @@ -124,12 +123,11 @@ definitions: createdAt: type: string duration: - allOf: - - $ref: '#/definitions/time.Duration' description: Uptime duration for this period + type: integer id: type: integer - nodeID: + node_id: type: integer timestamp: type: string @@ -137,9 +135,6 @@ definitions: description: True if this report followed a restart type: boolean type: object - gin.H: - additionalProperties: {} - type: object server.AccountCreationRequest: properties: public_key: @@ -171,7 +166,6 @@ definitions: interfaces: items: $ref: '#/definitions/db.Interface' - minItems: 1 type: array location: $ref: '#/definitions/db.Location' @@ -195,13 +189,6 @@ definitions: - twin_id type: object server.UpdateAccountRequest: - properties: - relays: - items: - type: string - type: array - rmb_enc_key: - type: string type: object server.UpdateFarmRequest: properties: @@ -236,19 +223,9 @@ definitions: - interfaces - location - resources - - secure_boot - serial_number - - virtualized type: object server.UptimeReportRequest: - properties: - timestamp: - type: string - uptime: - $ref: '#/definitions/time.Duration' - required: - - timestamp - - uptime type: object server.ZOSVersionRequest: properties: @@ -257,25 +234,8 @@ definitions: required: - version type: object - time.Duration: - enum: - - 1 - - 1000 - - 1000000 - - 1000000000 - - 60000000000 - type: integer - x-enum-varnames: - - Nanosecond - - Microsecond - - Millisecond - - Second - - Minute info: contact: {} - description: API for managing TFGrid node registration - title: Node Registrar API - version: "1.0" paths: /accounts: get: @@ -301,11 +261,13 @@ paths: "400": description: Invalid request schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object "404": description: Account not found schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object summary: Retrieve an account by twin ID or public key tags: - accounts @@ -330,11 +292,13 @@ paths: "400": description: Invalid request schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object "409": description: Account already exists schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object summary: Create new account tags: - accounts @@ -366,15 +330,23 @@ paths: "200": description: Account updated successfully schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object "400": description: Invalid request schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object + "401": + description: Unauthorized + schema: + additionalProperties: true + type: object "404": description: Account not found schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object summary: Update account details tags: - accounts @@ -412,11 +384,14 @@ paths: "200": description: List of farms schema: - $ref: '#/definitions/gin.H' + items: + $ref: '#/definitions/db.Farm' + type: array "400": description: Bad request schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object summary: List farms tags: - farms @@ -442,15 +417,22 @@ paths: "201": description: Farm created successfully schema: - $ref: '#/definitions/gin.H' + $ref: '#/definitions/db.Farm' "400": description: Invalid request schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object + "401": + description: Unauthorized + schema: + additionalProperties: true + type: object "409": description: Farm already exists schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object summary: Create new farm tags: - farms @@ -471,15 +453,17 @@ paths: "200": description: Farm details schema: - $ref: '#/definitions/gin.H' + $ref: '#/definitions/db.Farm' "400": description: Invalid farm ID schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object "404": description: Farm not found schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object summary: Get farm details tags: - farms @@ -510,15 +494,23 @@ paths: "200": description: Farm updated successfully schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object "400": description: Invalid request schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object + "401": + description: Unauthorized + schema: + additionalProperties: true + type: object "404": description: Farm not found schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object summary: Update farm tags: - farms @@ -564,11 +556,14 @@ paths: "200": description: List of nodes schema: - $ref: '#/definitions/gin.H' + items: + $ref: '#/definitions/db.Node' + type: array "400": description: Bad request schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object summary: List nodes tags: - nodes @@ -592,17 +587,24 @@ paths: - application/json responses: "201": - description: Node registered successfully + description: ID of the created node schema: - $ref: '#/definitions/gin.H' + type: integer "400": description: Invalid request schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object + "401": + description: Unauthorized + schema: + additionalProperties: true + type: object "409": description: Node already exists schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object summary: Register new node tags: - nodes @@ -623,15 +625,17 @@ paths: "200": description: Node details schema: - $ref: '#/definitions/gin.H' + $ref: '#/definitions/db.Node' "400": description: Invalid node ID schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object "404": description: Node not found schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object summary: Get node details tags: - nodes @@ -662,15 +666,23 @@ paths: "200": description: Node updated successfully schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object "400": description: Invalid request schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object + "401": + description: Unauthorized + schema: + additionalProperties: true + type: object "404": description: Node not found schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object summary: Update node tags: - nodes @@ -702,15 +714,23 @@ paths: "201": description: Uptime reported successfully schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object "400": description: Invalid request schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object + "401": + description: Unauthorized + schema: + additionalProperties: true + type: object "404": description: Node not found schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object summary: Report node uptime tags: - nodes @@ -721,17 +741,19 @@ paths: - application/json responses: "200": - description: OK + description: zos version schema: - $ref: '#/definitions/gin.H' + type: string "404": description: Not Found schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object "500": description: Internal Server Error schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object summary: Get ZOS Version tags: - ZOS @@ -757,19 +779,28 @@ paths: "200": description: OK schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object "400": description: Bad Request schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object + "401": + description: Unauthorized + schema: + additionalProperties: true + type: object "409": description: Conflict schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object "500": description: Internal Server Error schema: - $ref: '#/definitions/gin.H' + additionalProperties: true + type: object summary: Set ZOS Version tags: - ZOS diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index 05202e0..c4c1c06 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -8,8 +8,8 @@ import ( type Account struct { TwinID uint64 `gorm:"primaryKey;autoIncrement" json:"twin_id"` - Relays pq.StringArray `gorm:"type:text[];default:'{}'" json:"relays"` // Optional list of relay domains - RMBEncKey string `gorm:"type:text" json:"rmb_enc_key"` // Optional base64 encoded public key for rmb communication + Relays pq.StringArray `gorm:"type:text[];default:'{}'" json:"relays" swaggertype:"array,string"` // Optional list of relay domains + RMBEncKey string `gorm:"type:text" json:"rmb_enc_key"` // Optional base64 encoded public key for rmb communication CreatedAt time.Time UpdatedAt time.Time // The public key (ED25519 for nodes, ED25519 or SR25519 for farmers) in the more standard base64 since we are moving from substrate echo system? @@ -56,7 +56,7 @@ type Node struct { type UptimeReport struct { ID uint64 `gorm:"primaryKey;autoIncrement"` NodeID uint64 `gorm:"index" json:"node_id"` - Duration time.Duration // Uptime duration for this period + Duration time.Duration `swaggertype:"integer"` // Uptime duration for this period Timestamp time.Time `gorm:"index"` WasRestart bool // True if this report followed a restart CreatedAt time.Time From b12bc80899e0efa391e244220c9a9d304af613d1 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Mon, 10 Feb 2025 17:18:55 +0200 Subject: [PATCH 30/31] fix set zos version --- node-registrar/pkg/db/nodes.go | 21 ++++++++++++++------- node-registrar/pkg/server/handlers.go | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/node-registrar/pkg/db/nodes.go b/node-registrar/pkg/db/nodes.go index 0d01f4b..aa52f5d 100644 --- a/node-registrar/pkg/db/nodes.go +++ b/node-registrar/pkg/db/nodes.go @@ -7,6 +7,8 @@ import ( "gorm.io/gorm" ) +const ZOS4VersionKey = "zos_4" + // ListNodes retrieves all nodes from the database with applied filters and pagination func (db *Database) ListNodes(filter NodeFilter, limit Limit) (nodes []Node, err error) { query := db.gormDB.Model(&Node{}) @@ -81,16 +83,21 @@ func (db *Database) CreateUptimeReport(report *UptimeReport) error { func (db *Database) SetZOSVersion(version string) error { var current ZosVersion - err := db.gormDB.FirstOrCreate(¤t, ZosVersion{Key: "zos_4"}).Error - if err != nil { - return err - } + result := db.gormDB.Where(ZosVersion{Key: ZOS4VersionKey}).Attrs(ZosVersion{Version: version}).FirstOrCreate(¤t) - if current.Version == version { - return errors.New("version already set") + if result.Error != nil { + return result.Error } - return db.gormDB.Model(¤t).Update("version", version).Error + if result.RowsAffected == 0 { + if current.Version == version { + return errors.New("version already set") + } + return db.gormDB.Model(¤t). + Select("version"). + Update("version", version).Error + } + return nil } func (db *Database) GetZOSVersion() (string, error) { diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 6aca69e..add16be 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -706,7 +706,7 @@ type ZOSVersionRequest struct { // @Failure 401 {object} map[string]any "Unauthorized" // @Failure 409 {object} map[string]any "Conflict" // @Failure 500 {object} map[string]any "Internal Server Error" -// @Router /zos/version [post] +// @Router /zos/version [put] func (s *Server) setZOSVersionHandler(c *gin.Context) { ensureOwner(c, s.adminTwinID) if c.IsAborted() { From abcb34d772ecd238003e29d3298d6e8cd7836056 Mon Sep 17 00:00:00 2001 From: Sameh Abouel-saad Date: Mon, 10 Feb 2025 17:52:27 +0200 Subject: [PATCH 31/31] update swagger files --- node-registrar/docs/docs.go | 76 +++++++++++++++++++++++++++++--- node-registrar/docs/swagger.json | 74 +++++++++++++++++++++++++++++-- node-registrar/docs/swagger.yaml | 57 +++++++++++++++++++++++- 3 files changed, 195 insertions(+), 12 deletions(-) diff --git a/node-registrar/docs/docs.go b/node-registrar/docs/docs.go index d76e23c..d72f4cf 100644 --- a/node-registrar/docs/docs.go +++ b/node-registrar/docs/docs.go @@ -779,7 +779,7 @@ const docTemplate = `{ } } }, - "post": { + "put": { "description": "Sets the ZOS version", "consumes": [ "application/json" @@ -1120,7 +1120,18 @@ const docTemplate = `{ } }, "server.UpdateAccountRequest": { - "type": "object" + "type": "object", + "properties": { + "relays": { + "type": "array", + "items": { + "type": "string" + } + }, + "rmb_enc_key": { + "type": "string" + } + } }, "server.UpdateFarmRequest": { "type": "object", @@ -1173,7 +1184,19 @@ const docTemplate = `{ } }, "server.UptimeReportRequest": { - "type": "object" + "type": "object", + "required": [ + "timestamp", + "uptime" + ], + "properties": { + "timestamp": { + "type": "string" + }, + "uptime": { + "$ref": "#/definitions/time.Duration" + } + } }, "server.ZOSVersionRequest": { "type": "object", @@ -1185,18 +1208,57 @@ const docTemplate = `{ "type": "string" } } + }, + "time.Duration": { + "type": "integer", + "enum": [ + -9223372036854775808, + 9223372036854775807, + 1, + 1000, + 1000000, + 1000000000, + 60000000000, + 3600000000000, + -9223372036854775808, + 9223372036854775807, + 1, + 1000, + 1000000, + 1000000000, + 60000000000, + 3600000000000 + ], + "x-enum-varnames": [ + "minDuration", + "maxDuration", + "Nanosecond", + "Microsecond", + "Millisecond", + "Second", + "Minute", + "Hour", + "minDuration", + "maxDuration", + "Nanosecond", + "Microsecond", + "Millisecond", + "Second", + "Minute", + "Hour" + ] } } }` // 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 bd8e35f..2187c49 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": { @@ -768,7 +772,7 @@ } } }, - "post": { + "put": { "description": "Sets the ZOS version", "consumes": [ "application/json" @@ -1109,7 +1113,18 @@ } }, "server.UpdateAccountRequest": { - "type": "object" + "type": "object", + "properties": { + "relays": { + "type": "array", + "items": { + "type": "string" + } + }, + "rmb_enc_key": { + "type": "string" + } + } }, "server.UpdateFarmRequest": { "type": "object", @@ -1162,7 +1177,19 @@ } }, "server.UptimeReportRequest": { - "type": "object" + "type": "object", + "required": [ + "timestamp", + "uptime" + ], + "properties": { + "timestamp": { + "type": "string" + }, + "uptime": { + "$ref": "#/definitions/time.Duration" + } + } }, "server.ZOSVersionRequest": { "type": "object", @@ -1174,6 +1201,45 @@ "type": "string" } } + }, + "time.Duration": { + "type": "integer", + "enum": [ + -9223372036854775808, + 9223372036854775807, + 1, + 1000, + 1000000, + 1000000000, + 60000000000, + 3600000000000, + -9223372036854775808, + 9223372036854775807, + 1, + 1000, + 1000000, + 1000000000, + 60000000000, + 3600000000000 + ], + "x-enum-varnames": [ + "minDuration", + "maxDuration", + "Nanosecond", + "Microsecond", + "Millisecond", + "Second", + "Minute", + "Hour", + "minDuration", + "maxDuration", + "Nanosecond", + "Microsecond", + "Millisecond", + "Second", + "Minute", + "Hour" + ] } } } \ No newline at end of file diff --git a/node-registrar/docs/swagger.yaml b/node-registrar/docs/swagger.yaml index f28575f..9cda3fd 100644 --- a/node-registrar/docs/swagger.yaml +++ b/node-registrar/docs/swagger.yaml @@ -1,3 +1,4 @@ +basePath: /v1 definitions: db.Account: properties: @@ -189,6 +190,13 @@ definitions: - twin_id type: object server.UpdateAccountRequest: + properties: + relays: + items: + type: string + type: array + rmb_enc_key: + type: string type: object server.UpdateFarmRequest: properties: @@ -226,6 +234,14 @@ definitions: - serial_number type: object server.UptimeReportRequest: + properties: + timestamp: + type: string + uptime: + $ref: '#/definitions/time.Duration' + required: + - timestamp + - uptime type: object server.ZOSVersionRequest: properties: @@ -234,8 +250,47 @@ definitions: required: - version type: object + time.Duration: + enum: + - -9223372036854775808 + - 9223372036854775807 + - 1 + - 1000 + - 1000000 + - 1000000000 + - 60000000000 + - 3600000000000 + - -9223372036854775808 + - 9223372036854775807 + - 1 + - 1000 + - 1000000 + - 1000000000 + - 60000000000 + - 3600000000000 + type: integer + x-enum-varnames: + - minDuration + - maxDuration + - Nanosecond + - Microsecond + - Millisecond + - Second + - Minute + - Hour + - minDuration + - maxDuration + - Nanosecond + - Microsecond + - Millisecond + - Second + - Minute + - Hour info: contact: {} + description: API for managing TFGrid node registration + title: Node Registrar API + version: "1.0" paths: /accounts: get: @@ -757,7 +812,7 @@ paths: summary: Get ZOS Version tags: - ZOS - post: + put: consumes: - application/json description: Sets the ZOS version