Skip to content

Commit

Permalink
feat: create test apis + db update + create instance
Browse files Browse the repository at this point in the history
  • Loading branch information
mojtaba-esk committed Jan 22, 2025
1 parent 7f5ddc8 commit 3c0bd3b
Show file tree
Hide file tree
Showing 28 changed files with 780 additions and 142 deletions.
67 changes: 40 additions & 27 deletions cmd/api/api.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"context"
"fmt"

"github.com/celestiaorg/knuu/internal/api/v1"
Expand Down Expand Up @@ -66,16 +67,6 @@ func NewAPICmd() *cobra.Command {
}

func runAPIServer(cmd *cobra.Command, args []string) error {
port, err := cmd.Flags().GetInt(flagPort)
if err != nil {
return fmt.Errorf("failed to get port: %v", err)
}

logLevel, err := cmd.Flags().GetString(flagLogLevel)
if err != nil {
return fmt.Errorf("failed to get log level: %v", err)
}

dbOpts, err := getDBOptions(cmd.Flags())
if err != nil {
return fmt.Errorf("failed to get database options: %v", err)
Expand All @@ -86,29 +77,16 @@ func runAPIServer(cmd *cobra.Command, args []string) error {
return fmt.Errorf("failed to connect to database: %v", err)
}

secretKey, err := cmd.Flags().GetString(flagSecretKey)
apiOpts, err := getAPIOptions(cmd.Flags())
if err != nil {
return fmt.Errorf("failed to get secret key: %v", err)
return fmt.Errorf("failed to get API options: %v", err)
}

adminUser, err := cmd.Flags().GetString(flagAdminUser)
apiServer, err := api.New(context.Background(), db, apiOpts)
if err != nil {
return fmt.Errorf("failed to get admin user: %v", err)
return fmt.Errorf("failed to create API server: %v", err)
}

adminPass, err := cmd.Flags().GetString(flagAdminPass)
if err != nil {
return fmt.Errorf("failed to get admin password: %v", err)
}

apiServer := api.New(db, api.Options{
Port: port,
LogMode: logLevel,
SecretKey: secretKey,
AdminUser: adminUser,
AdminPass: adminPass,
})

return apiServer.Start()
}

Expand Down Expand Up @@ -146,3 +124,38 @@ func getDBOptions(flags *pflag.FlagSet) (database.Options, error) {
Port: dbPort,
}, nil
}

func getAPIOptions(flags *pflag.FlagSet) (api.Options, error) {
port, err := flags.GetInt(flagPort)
if err != nil {
return api.Options{}, fmt.Errorf("failed to get port: %v", err)
}

logLevel, err := flags.GetString(flagLogLevel)
if err != nil {
return api.Options{}, fmt.Errorf("failed to get log level: %v", err)
}

secretKey, err := flags.GetString(flagSecretKey)
if err != nil {
return api.Options{}, fmt.Errorf("failed to get secret key: %v", err)
}

adminUser, err := flags.GetString(flagAdminUser)
if err != nil {
return api.Options{}, fmt.Errorf("failed to get admin user: %v", err)
}

adminPass, err := flags.GetString(flagAdminPass)
if err != nil {
return api.Options{}, fmt.Errorf("failed to get admin password: %v", err)
}

return api.Options{
Port: port,
LogMode: logLevel,
SecretKey: secretKey,
AdminUser: adminUser,
AdminPass: adminPass,
}, nil
}
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ go 1.22.5
require (
github.com/celestiaorg/bittwister v0.0.0-20231213180407-65cdbaf5b8c7
github.com/gin-gonic/gin v1.10.0
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/golang-jwt/jwt/v4 v4.5.1
github.com/google/uuid v1.6.0
github.com/minio/minio-go/v7 v7.0.74
github.com/rs/cors v1.11.1
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.29.0
gopkg.in/yaml.v2 v2.4.0
Expand Down Expand Up @@ -78,7 +80,6 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rs/xid v1.5.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
Expand Down
53 changes: 48 additions & 5 deletions internal/api/v1/api.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package api

import (
"context"
"fmt"
"net/http"

"github.com/celestiaorg/knuu/internal/api/v1/handlers"
"github.com/celestiaorg/knuu/internal/api/v1/middleware"
"github.com/celestiaorg/knuu/internal/api/v1/services"
"github.com/celestiaorg/knuu/internal/database/models"
"github.com/celestiaorg/knuu/internal/database/repos"

"github.com/gin-gonic/gin"
Expand All @@ -29,21 +31,44 @@ type Options struct {
LogMode string // gin.DebugMode, gin.ReleaseMode(default), gin.TestMode
OriginAllowed string
SecretKey string

AdminUser string // default admin username
AdminPass string // default admin password
}

func New(db *gorm.DB, opts Options) *API {
func New(ctx context.Context, db *gorm.DB, opts Options) (*API, error) {
opts = setDefaults(opts)
gin.SetMode(opts.LogMode)

rt := gin.Default()

auth := middleware.NewAuth(opts.SecretKey)
uh, err := getUserHandler(ctx, opts, db, auth)
if err != nil {
return nil, err
}

public := rt.Group("/")
{
uh := handlers.NewUserHandler(services.NewUserService(opts.SecretKey, repos.NewUserRepository(db)))
public.POST(pathsUserRegister, uh.Register)
public.POST(pathsUserLogin, uh.Login)
}
protected := rt.Group("/", middleware.AuthMiddleware())

protected := rt.Group("/", auth.AuthMiddleware())
{
protected.POST(pathsUserRegister, auth.RequireRole(models.RoleAdmin), uh.Register)

th, err := getTestHandler(ctx, db)
if err != nil {
return nil, err
}

protected.POST(pathsTests, th.CreateTest)
// protected.GET(pathsTestDetails, th.GetTestDetails)
// protected.GET(pathsTestInstances, th.GetInstances)
protected.GET(pathsTestInstanceDetails, th.GetInstance)
protected.POST(pathsTestInstanceDetails, th.CreateInstance) // Need to do something about updating an instance
// protected.POST(pathsTestInstanceExecute, th.ExecuteInstance)
}

_ = protected

Expand All @@ -59,7 +84,7 @@ func New(db *gorm.DB, opts Options) *API {
public.GET("/", a.IndexPage)
}

return a
return a, nil
}

func (a *API) Start() error {
Expand Down Expand Up @@ -103,3 +128,21 @@ func handleOrigin(router *gin.Engine, originAllowed string) http.Handler {
AllowedMethods: methodsOk,
}).Handler(router)
}

func getUserHandler(ctx context.Context, opts Options, db *gorm.DB, auth *middleware.Auth) (*handlers.UserHandler, error) {
us, err := services.NewUserService(ctx, opts.AdminUser, opts.AdminPass, repos.NewUserRepository(db))
if err != nil {
return nil, err
}

return handlers.NewUserHandler(us, auth), nil
}

func getTestHandler(ctx context.Context, db *gorm.DB) (*handlers.TestHandler, error) {
ts, err := services.NewTestService(ctx, repos.NewTestRepository(db))
if err != nil {
return nil, err
}

return handlers.NewTestHandler(ts), nil
}
44 changes: 44 additions & 0 deletions internal/api/v1/handlers/instance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package handlers

import (
"net/http"

"github.com/celestiaorg/knuu/internal/api/v1/services"
"github.com/gin-gonic/gin"
)

func (h *TestHandler) CreateInstance(c *gin.Context) {
user, err := getUserFromContext(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}

var input services.Instance
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"})
return
}

err = h.testService.CreateInstance(c.Request.Context(), user.ID, &input)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, gin.H{"message": "Instance created successfully"})
}

func (h *TestHandler) GetInstance(c *gin.Context) {
user, err := getUserFromContext(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}

instance, err := h.testService.GetInstance(c.Request.Context(), user.ID, c.Param("scope"), c.Param("instance_name"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, instance)
}
38 changes: 38 additions & 0 deletions internal/api/v1/handlers/test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package handlers

import (
"net/http"

"github.com/celestiaorg/knuu/internal/api/v1/services"
"github.com/celestiaorg/knuu/internal/database/models"
"github.com/gin-gonic/gin"
)

type TestHandler struct {
testService *services.TestService
}

func NewTestHandler(ts *services.TestService) *TestHandler {
return &TestHandler{testService: ts}
}

func (h *TestHandler) CreateTest(c *gin.Context) {
user, err := getUserFromContext(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}

var input models.Test
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"})
return
}

input.UserID = user.ID
if err := h.testService.Create(c.Request.Context(), &input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, gin.H{"message": "Test created successfully"})
}
1 change: 0 additions & 1 deletion internal/api/v1/handlers/test_handler.go

This file was deleted.

5 changes: 5 additions & 0 deletions internal/api/v1/handlers/token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package handlers

// Users can request for new tokens with some permissions
// A user can have multiple tokens with different permissions
// A token can be revoked by the user or by the admin
1 change: 0 additions & 1 deletion internal/api/v1/handlers/token_handler.go

This file was deleted.

21 changes: 16 additions & 5 deletions internal/api/v1/handlers/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package handlers
import (
"net/http"

"github.com/celestiaorg/knuu/internal/api/v1/middleware"
"github.com/celestiaorg/knuu/internal/api/v1/services"
"github.com/celestiaorg/knuu/internal/database/models"

Expand All @@ -11,10 +12,14 @@ import (

type UserHandler struct {
userService services.UserService
auth *middleware.Auth
}

func NewUserHandler(userService services.UserService) *UserHandler {
return &UserHandler{userService: userService}
func NewUserHandler(userService services.UserService, auth *middleware.Auth) *UserHandler {
return &UserHandler{
userService: userService,
auth: auth,
}
}

func (h *UserHandler) Register(c *gin.Context) {
Expand All @@ -24,12 +29,12 @@ func (h *UserHandler) Register(c *gin.Context) {
return
}

user, err := h.userService.Register(&input)
_, err := h.userService.Register(c.Request.Context(), &input)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, user)
c.JSON(http.StatusCreated, gin.H{"message": "User registered successfully"})
}

func (h *UserHandler) Login(c *gin.Context) {
Expand All @@ -42,10 +47,16 @@ func (h *UserHandler) Login(c *gin.Context) {
return
}

token, err := h.userService.Authenticate(input.Username, input.Password)
user, err := h.userService.Authenticate(c.Request.Context(), input.Username, input.Password)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
return
}

token, err := h.auth.GenerateToken(user)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"token": token})
}
21 changes: 21 additions & 0 deletions internal/api/v1/handlers/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package handlers

import (
"errors"

"github.com/celestiaorg/knuu/internal/api/v1/middleware"
"github.com/celestiaorg/knuu/internal/database/models"
"github.com/gin-gonic/gin"
)

func getUserFromContext(c *gin.Context) (*models.User, error) {
user, ok := c.Get(middleware.UserContextKey)
if !ok {
return nil, errors.New("user not found in context")
}
authUser, ok := user.(*models.User)
if !ok {
return nil, errors.New("invalid user data in context")
}
return authUser, nil
}
Loading

0 comments on commit 3c0bd3b

Please sign in to comment.