From 244a4fbe59c0a7331fddecd1ce7060eb48f76b3e Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Wed, 19 Mar 2025 14:49:57 +0200 Subject: [PATCH 1/2] refactor node registrar --- node-registrar/cmds/main.go | 28 +++++++------- node-registrar/pkg/db/db.go | 10 ++--- node-registrar/pkg/db/models.go | 2 +- node-registrar/pkg/server/handlers.go | 37 ++++++++----------- node-registrar/pkg/server/middlewares.go | 17 +++++++-- node-registrar/pkg/server/routes.go | 46 +++++++++++------------ node-registrar/pkg/server/server.go | 26 ++++++++++--- node-registrar/pkg/server/sigverifier.go | 47 ++++++++++++++++++++++++ node-registrar/pkg/server/util.go | 31 ---------------- 9 files changed, 139 insertions(+), 105 deletions(-) create mode 100644 node-registrar/pkg/server/sigverifier.go delete mode 100644 node-registrar/pkg/server/util.go diff --git a/node-registrar/cmds/main.go b/node-registrar/cmds/main.go index bbb0f63..63afe22 100644 --- a/node-registrar/cmds/main.go +++ b/node-registrar/cmds/main.go @@ -5,9 +5,7 @@ import ( "fmt" "net" "os" - "os/signal" "strings" - "syscall" "github.com/pkg/errors" "github.com/rs/zerolog" @@ -57,7 +55,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.Uint64Var(&f.adminTwinID, "admin-twin-id", 1, "admin twin ID") flag.Parse() f.SqlLogLevel = logger.LogLevel(sqlLogLevel) @@ -71,11 +69,11 @@ func Run() error { return err } - log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) - zerolog.SetGlobalLevel(zerolog.InfoLevel) + logLevel := zerolog.InfoLevel if f.debug { - zerolog.SetGlobalLevel(zerolog.DebugLevel) + logLevel = zerolog.DebugLevel } + log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).Level(logLevel).With().Timestamp().Logger() db, err := db.NewDB(f.Config) if err != nil { @@ -89,17 +87,11 @@ func Run() error { } }() - s, err := server.NewServer(db, f.network, f.adminTwinID) - if err != nil { - return errors.Wrap(err, "failed to start gin server") - } - - quit := make(chan os.Signal, 1) - signal.Notify(quit, os.Interrupt, syscall.SIGTERM) + s := server.NewServer(db, f.network, f.adminTwinID) log.Info().Msgf("server is running on port :%d", f.serverPort) - err = s.Run(quit, fmt.Sprintf("%s:%d", f.domain, f.serverPort)) + err = s.Run(fmt.Sprintf("%s:%d", f.domain, f.serverPort)) if err != nil { return errors.Wrap(err, "failed to run gin server") } @@ -115,6 +107,14 @@ func (f flags) validate() error { if strings.TrimSpace(f.domain) == "" { return errors.New("invalid domain name, domain name should not be empty") } + + if f.SqlLogLevel < 1 || f.SqlLogLevel > 4 { + return errors.Errorf("invalid sql log level %d, sql log level should be in the range 1-4", f.SqlLogLevel) + } + if f.adminTwinID == 0 { + return errors.Errorf("invalid admin twin id %d, admin twin id should not be 0", f.adminTwinID) + } + if _, err := net.LookupHost(f.domain); err != nil { return errors.Wrapf(err, "invalid domain %s", f.domain) } diff --git a/node-registrar/pkg/db/db.go b/node-registrar/pkg/db/db.go index 25c8b32..6e32ca9 100644 --- a/node-registrar/pkg/db/db.go +++ b/node-registrar/pkg/db/db.go @@ -90,24 +90,24 @@ func (c Config) Validate() error { } if net.ParseIP(c.PostgresHost) == nil { - if _, err := net.LookupHost(c.PostgresHost); err == nil { + if _, err := net.LookupHost(c.PostgresHost); err != nil { return errors.Wrapf(err, "invalid postgres host %s, failed to parse or lookup host", c.PostgresHost) } } - if c.PostgresPort < 1 && c.PostgresPort > 65535 { + if c.PostgresPort < 1 || c.PostgresPort > 65535 { return errors.Errorf("invalid postgres port %d, postgres port should be in the valid port range 1–65535", c.PostgresPort) } - if strings.TrimSpace(c.DBName) == "" { + if len(strings.TrimSpace(c.DBName)) == 0 { return errors.New("invalid database name, database name should not be empty") } - if strings.TrimSpace(c.PostgresUser) == "" { + if len(strings.TrimSpace(c.PostgresUser)) == 0 { return errors.New("invalid postgres user, postgres user should not be empty") } - if strings.TrimSpace(c.PostgresPassword) == "" { + if len(strings.TrimSpace(c.PostgresPassword)) == 0 { return errors.New("invalid postgres password, postgres password should not be empty") } diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index 3540ef3..5c3a815 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -23,7 +23,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" binding:"alphanum,required"` - TwinID uint64 `json:"twin_id" gorm:"not null;check:twin_id > 0"` // Farmer account reference + TwinID uint64 `json:"twin_id" binding:"required" gorm:"not null;check:twin_id > 0"` // Farmer account reference StellarAddress string `json:"stellar_address" binding:"required,startswith=G,len=56,alphanum,uppercase"` Dedicated bool `json:"dedicated"` CreatedAt time.Time `json:"created_at"` diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index d4cc2e2..0e2534e 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -11,12 +11,10 @@ import ( "github.com/gin-gonic/gin" "github.com/lib/pq" - "github.com/rs/zerolog/log" "github.com/threefoldtech/tfgrid4-sdk-go/node-registrar/pkg/db" ) const ( - PubKeySize = 32 MaxTimestampDelta = 2 * time.Second ) @@ -78,13 +76,14 @@ func (s Server) getFarmHandler(c *gin.Context) { farm, err := s.db.GetFarm(id) if err != nil { - status := http.StatusBadRequest + status := http.StatusInternalServerError if errors.Is(err, db.ErrRecordNotFound) { status = http.StatusNotFound } c.JSON(status, gin.H{"error": err.Error()}) + return } c.JSON(http.StatusOK, farm) @@ -117,7 +116,7 @@ func (s Server) createFarmHandler(c *gin.Context) { farmID, err := s.db.CreateFarm(farm) if err != nil { - status := http.StatusBadRequest + status := http.StatusInternalServerError if errors.Is(err, db.ErrRecordAlreadyExists) { status = http.StatusConflict @@ -148,7 +147,7 @@ type UpdateFarmRequest struct { // @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) { +func (s Server) updateFarmHandler(c *gin.Context) { var req UpdateFarmRequest farmID := c.Param("farm_id") @@ -186,7 +185,7 @@ func (s Server) updateFarmsHandler(c *gin.Context) { (len(req.StellarAddress) != 0 && existingFarm.StellarAddress != req.StellarAddress) { err = s.db.UpdateFarm(id, req.FarmName, req.StellarAddress) if err != nil { - status := http.StatusBadRequest + status := http.StatusInternalServerError if errors.Is(err, db.ErrRecordNotFound) { status = http.StatusNotFound @@ -229,7 +228,7 @@ func (s Server) listNodesHandler(c *gin.Context) { nodes, err := s.db.ListNodes(filter, limit) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } @@ -262,7 +261,7 @@ func (s Server) getNodeHandler(c *gin.Context) { return } - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } @@ -319,7 +318,7 @@ func (s Server) registerNodeHandler(c *gin.Context) { nodeID, err := s.db.RegisterNode(node) if err != nil { - status := http.StatusBadRequest + status := http.StatusInternalServerError if errors.Is(err, db.ErrRecordAlreadyExists) { status = http.StatusConflict @@ -382,7 +381,6 @@ func (s *Server) updateNodeHandler(c *gin.Context) { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid request body"}) return } - log.Debug().Any("req", req).Send() updatedNode := db.Node{ FarmID: req.FarmID, @@ -540,11 +538,13 @@ func (s *Server) createAccountHandler(c *gin.Context) { publicKeyBytes, err := base64.StdEncoding.DecodeString(req.PublicKey) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid public key format"}) + return } // 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)}) + return } // Verify signature of the challenge err = verifySignature(publicKeyBytes, challenge, signatureBytes) @@ -572,13 +572,6 @@ 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 pq.StringArray `json:"relays"` RMBEncKey string `json:"rmb_enc_key"` @@ -669,7 +662,7 @@ func (s *Server) getAccountHandler(c *gin.Context) { account, err := s.db.GetAccount(twinID) if err != nil { - if err == db.ErrRecordNotFound { + if errors.Is(err, db.ErrRecordNotFound) { c.JSON(http.StatusNotFound, gin.H{"error": "account not found"}) return } @@ -684,7 +677,7 @@ func (s *Server) getAccountHandler(c *gin.Context) { if publicKeyParam != "" { account, err := s.db.GetAccountByPublicKey(publicKeyParam) if err != nil { - if err == db.ErrRecordNotFound { + if errors.Is(err, db.ErrRecordNotFound) { c.JSON(http.StatusNotFound, gin.H{"error": "account not found"}) return } @@ -759,7 +752,7 @@ func (s *Server) getZOSVersionHandler(c *gin.Context) { c.JSON(http.StatusOK, version) } -// Helper function to validate public key format +// Helper function to validate public key length func isValidPublicKey(publicKeyBase64 string) bool { publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64) if err != nil { @@ -771,8 +764,8 @@ func isValidPublicKey(publicKeyBase64 string) bool { // 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 { + authTwinID := c.Request.Context().Value(twinIDKey{}) + if authTwinID == nil { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "not authorized"}) return } diff --git a/node-registrar/pkg/server/middlewares.go b/node-registrar/pkg/server/middlewares.go index ed198e6..0c65ffc 100644 --- a/node-registrar/pkg/server/middlewares.go +++ b/node-registrar/pkg/server/middlewares.go @@ -1,6 +1,7 @@ package server import ( + "context" "encoding/base64" "errors" "fmt" @@ -10,12 +11,16 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/rs/zerolog/log" "github.com/threefoldtech/tfgrid4-sdk-go/node-registrar/pkg/db" ) +// twinKeyID is where the twin key is stored +type twinIDKey struct{} + const ( AuthHeader = "X-Auth" - ChallengeValidity = 5 * time.Minute + ChallengeValidity = 1 * time.Minute ) // AuthMiddleware is a middleware function that authenticates incoming requests based on the X-Auth header. @@ -26,7 +31,6 @@ const ( // 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 @@ -47,6 +51,7 @@ func (s *Server) AuthMiddleware() gin.HandlerFunc { // Decode and validate challenge challenge, err := base64.StdEncoding.DecodeString(challengeB64) if err != nil { + log.Debug().Err(err).Msg("failed to deconde challenge") abortWithError(c, http.StatusBadRequest, "Invalid challenge encoding") return } @@ -62,6 +67,7 @@ func (s *Server) AuthMiddleware() gin.HandlerFunc { // Validate timestamp timestamp, err := strconv.ParseInt(timestampStr, 10, 64) if err != nil { + log.Debug().Err(err).Msg("invalid timestamp") abortWithError(c, http.StatusBadRequest, "Invalid timestamp") return } @@ -73,36 +79,41 @@ func (s *Server) AuthMiddleware() gin.HandlerFunc { twinID, err := strconv.ParseUint(twinIDStr, 10, 64) if err != nil { + log.Debug().Err(err).Msg("invalid twin id format") abortWithError(c, http.StatusBadRequest, "Invalid twin ID format") return } account, err := s.db.GetAccount(twinID) if err != nil { + log.Debug().Err(err).Uint64("twinID", twinID).Msg("failed to get account") handleDatabaseError(c, err) return } storedPK, err := base64.StdEncoding.DecodeString(account.PublicKey) if err != nil { + log.Debug().Err(err).Msg("failed to get invalid stored public key") abortWithError(c, http.StatusBadRequest, fmt.Sprintf("invalid stored public key: %v", err)) return } sig, err := base64.StdEncoding.DecodeString(signatureB64) if err != nil { + log.Debug().Err(err).Msg("invalid signature encoding") abortWithError(c, http.StatusBadRequest, "Invalid signature encoding") return } // Verify signature (supports both ED25519 and SR25519) if err := verifySignature(storedPK, challenge, sig); err != nil { + log.Debug().Err(err).Msg("signature verification failed") 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) + c.Request = c.Request.WithContext(context.WithValue(c.Request.Context(), twinIDKey{}, twinID)) c.Next() } } diff --git a/node-registrar/pkg/server/routes.go b/node-registrar/pkg/server/routes.go index 8b48a8d..376c307 100644 --- a/node-registrar/pkg/server/routes.go +++ b/node-registrar/pkg/server/routes.go @@ -21,39 +21,39 @@ func (s *Server) SetupRoutes() { })) s.router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) - v1 := s.router.Group("v1") + v1 := s.router.Group("/api/v1") // farms routes - farmRoutes := v1.Group("farms") - farmRoutes.GET("/", s.listFarmsHandler) - farmRoutes.GET("/:farm_id", s.getFarmHandler) + publicFarmRoutes := v1.Group("farms") + publicFarmRoutes.GET("/", s.listFarmsHandler) + publicFarmRoutes.GET("/:farm_id", s.getFarmHandler) // protected by farmer key - farmRoutes.Use(s.AuthMiddleware()) - farmRoutes.POST("/", s.createFarmHandler) - farmRoutes.PATCH("/:farm_id", s.updateFarmsHandler) + protectedFarmRoutes := v1.Group("farms", s.AuthMiddleware()) + protectedFarmRoutes.POST("/", s.createFarmHandler) + protectedFarmRoutes.PATCH("/:farm_id", s.updateFarmHandler) // nodes routes - nodeRoutes := v1.Group("nodes") - nodeRoutes.GET("/", s.listNodesHandler) - nodeRoutes.GET("/:node_id", s.getNodeHandler) + publicNodeRoutes := v1.Group("nodes") + publicNodeRoutes.GET("/", s.listNodesHandler) + publicNodeRoutes.GET("/:node_id", s.getNodeHandler) // 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) + protectedNodeRoutes := v1.Group("nodes", s.AuthMiddleware()) + protectedNodeRoutes.POST("/", s.registerNodeHandler) + protectedNodeRoutes.PATCH("/:node_id", s.updateNodeHandler) + protectedNodeRoutes.POST("/:node_id/uptime", s.uptimeReportHandler) // Account routes - accountRoutes := v1.Group("accounts") - accountRoutes.POST("/", s.createAccountHandler) - accountRoutes.GET("/", s.getAccountHandler) + publicAccountRoutes := v1.Group("accounts") + publicAccountRoutes.POST("/", s.createAccountHandler) + publicAccountRoutes.GET("/", s.getAccountHandler) // protected by farmer key - accountRoutes.Use(s.AuthMiddleware()) - accountRoutes.PATCH("/:twin_id", s.updateAccountHandler) + protectedAccountRoutes := v1.Group("accounts", s.AuthMiddleware()) + protectedAccountRoutes.PATCH("/:twin_id", s.updateAccountHandler) // zOS Version endpoints - zosRoutes := v1.Group("/zos") - zosRoutes.GET("/version", s.getZOSVersionHandler) + publicZosRoutes := v1.Group("/zos") + publicZosRoutes.GET("/version", s.getZOSVersionHandler) // protected by admin key - zosRoutes.Use(s.AuthMiddleware()) - zosRoutes.PUT("/version", s.setZOSVersionHandler) + protectedZosRoutes := v1.Group("/zos", s.AuthMiddleware()) + protectedZosRoutes.PUT("/version", s.setZOSVersionHandler) } diff --git a/node-registrar/pkg/server/server.go b/node-registrar/pkg/server/server.go index 2cf0344..45880ff 100644 --- a/node-registrar/pkg/server/server.go +++ b/node-registrar/pkg/server/server.go @@ -4,10 +4,13 @@ import ( "context" "net/http" "os" + "os/signal" "sync" "syscall" + "time" "github.com/gin-gonic/gin" + "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/threefoldtech/tfgrid4-sdk-go/node-registrar/pkg/db" ) @@ -19,21 +22,24 @@ type Server struct { adminTwinID uint64 } -func NewServer(db db.Database, network string, adminTwinID uint64) (s Server, err error) { +func NewServer(db db.Database, network string, adminTwinID uint64) Server { router := gin.Default() - s = Server{router, db, network, adminTwinID} - s.SetupRoutes() + server := Server{router, db, network, adminTwinID} + server.SetupRoutes() - return + return server } -func (s Server) Run(quit chan os.Signal, addr string) error { +func (s Server) Run(addr string) error { server := &http.Server{ Addr: addr, Handler: s.router, } + quit := make(chan os.Signal, 1) + signal.Notify(quit, os.Interrupt, syscall.SIGTERM) + var wg sync.WaitGroup wg.Add(1) @@ -41,8 +47,11 @@ func (s Server) Run(quit chan os.Signal, addr string) error { defer wg.Done() <-quit + context, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + log.Info().Msg("server is shutting down") - err := server.Shutdown(context.Background()) + err := server.Shutdown(context) if err != nil { log.Error().Err(err).Msg("failed to shut down server gracefully") } @@ -51,6 +60,11 @@ func (s Server) Run(quit chan os.Signal, addr string) error { err := server.ListenAndServe() if err != nil { quit <- syscall.SIGINT + if errors.Is(err, http.ErrServerClosed) { + log.Info().Msg("server stopped gracefully") + } else { + log.Error().Err(err).Msg("server stopped unexpectedly") + } } wg.Wait() diff --git a/node-registrar/pkg/server/sigverifier.go b/node-registrar/pkg/server/sigverifier.go new file mode 100644 index 0000000..8b80c99 --- /dev/null +++ b/node-registrar/pkg/server/sigverifier.go @@ -0,0 +1,47 @@ +package server + +import ( + "github.com/pkg/errors" + "github.com/vedhavyas/go-subkey/v2" + "github.com/vedhavyas/go-subkey/v2/ed25519" + "github.com/vedhavyas/go-subkey/v2/sr25519" +) + +const PubKeySize = 32 // ED25519/SR25519 public key size + +var ( + ErrInvalidInput = errors.New("invalid input") + ErrVerifyFailed = errors.New("signature verification failed") +) + +func verifySignature(publicKey, challenge, signature []byte) error { + // Verify public key length + if len(publicKey) != PubKeySize { + return errors.Wrapf(ErrInvalidInput, "invalid public key size: expected %d, got %d", PubKeySize, len(publicKey)) + } + + if len(challenge) == 0 { + return errors.Wrap(ErrInvalidInput, "invalid challenge size, not expected to be zero") + } + + if len(signature) == 0 { + return errors.Wrap(ErrInvalidInput, "invalid signature size, not expected to be zero") + } + + // Try ED25519 verification first + if verifyWithScheme(ed25519.Scheme{}, publicKey, challenge, signature) || + // Fallback to SR25519 verification + verifyWithScheme(sr25519.Scheme{}, publicKey, challenge, signature) { + return nil + } + + return ErrVerifyFailed +} + +func verifyWithScheme(scheme subkey.Scheme, publicKey, challenge, signature []byte) bool { + key, err := scheme.FromPublicKey(publicKey) + if err != nil { + return false + } + return key.Verify(challenge, signature) +} diff --git a/node-registrar/pkg/server/util.go b/node-registrar/pkg/server/util.go deleted file mode 100644 index 3c31770..0000000 --- a/node-registrar/pkg/server/util.go +++ /dev/null @@ -1,31 +0,0 @@ -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 ed753372625ef7f65d0f6518e3f01cba57294ee0 Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Tue, 25 Mar 2025 12:07:26 +0200 Subject: [PATCH 2/2] support both /api/v1 and /v1 prefixes in registrar --- node-registrar/pkg/server/routes.go | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/node-registrar/pkg/server/routes.go b/node-registrar/pkg/server/routes.go index 376c307..e284d38 100644 --- a/node-registrar/pkg/server/routes.go +++ b/node-registrar/pkg/server/routes.go @@ -4,6 +4,7 @@ import ( "time" "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" _ "github.com/threefoldtech/tfgrid4-sdk-go/node-registrar/docs" swaggerFiles "github.com/swaggo/files" @@ -21,39 +22,43 @@ func (s *Server) SetupRoutes() { })) s.router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) - v1 := s.router.Group("/api/v1") + s.registerRoutes(s.router.Group("/api/v1")) + s.registerRoutes(s.router.Group("/v1")) +} + +func (s *Server) registerRoutes(r *gin.RouterGroup) { // farms routes - publicFarmRoutes := v1.Group("farms") + publicFarmRoutes := r.Group("farms") publicFarmRoutes.GET("/", s.listFarmsHandler) publicFarmRoutes.GET("/:farm_id", s.getFarmHandler) // protected by farmer key - protectedFarmRoutes := v1.Group("farms", s.AuthMiddleware()) + protectedFarmRoutes := r.Group("farms", s.AuthMiddleware()) protectedFarmRoutes.POST("/", s.createFarmHandler) protectedFarmRoutes.PATCH("/:farm_id", s.updateFarmHandler) // nodes routes - publicNodeRoutes := v1.Group("nodes") + publicNodeRoutes := r.Group("nodes") publicNodeRoutes.GET("/", s.listNodesHandler) publicNodeRoutes.GET("/:node_id", s.getNodeHandler) // protected by node key - protectedNodeRoutes := v1.Group("nodes", s.AuthMiddleware()) + protectedNodeRoutes := r.Group("nodes", s.AuthMiddleware()) protectedNodeRoutes.POST("/", s.registerNodeHandler) protectedNodeRoutes.PATCH("/:node_id", s.updateNodeHandler) protectedNodeRoutes.POST("/:node_id/uptime", s.uptimeReportHandler) // Account routes - publicAccountRoutes := v1.Group("accounts") + publicAccountRoutes := r.Group("accounts") publicAccountRoutes.POST("/", s.createAccountHandler) publicAccountRoutes.GET("/", s.getAccountHandler) // protected by farmer key - protectedAccountRoutes := v1.Group("accounts", s.AuthMiddleware()) + protectedAccountRoutes := r.Group("accounts", s.AuthMiddleware()) protectedAccountRoutes.PATCH("/:twin_id", s.updateAccountHandler) // zOS Version endpoints - publicZosRoutes := v1.Group("/zos") + publicZosRoutes := r.Group("/zos") publicZosRoutes.GET("/version", s.getZOSVersionHandler) // protected by admin key - protectedZosRoutes := v1.Group("/zos", s.AuthMiddleware()) + protectedZosRoutes := r.Group("/zos", s.AuthMiddleware()) protectedZosRoutes.PUT("/version", s.setZOSVersionHandler) }