diff --git a/core/apps/api.go b/core/apps/api.go index 4f25e6b6..6016a528 100644 --- a/core/apps/api.go +++ b/core/apps/api.go @@ -50,7 +50,7 @@ func (app *Api) Start() { // http server app.srv = &http.Server{ - Handler: app.app, + Handler: app.GetHttpServerHandler(), Addr: address, } @@ -85,6 +85,10 @@ func (app *Api) Stop() { } } +func (app *Api) GetHttpServerHandler() http.Handler { + return controllers.GetGlobalFizzWrapper().GetFizz() +} + func (app *Api) GetGinEngine() *gin.Engine { return app.app } diff --git a/core/apps/server.go b/core/apps/server.go index 3724ebec..bac4b25a 100644 --- a/core/apps/server.go +++ b/core/apps/server.go @@ -2,13 +2,14 @@ package apps import ( "fmt" + "net/http" + _ "net/http/pprof" + "sync" + "github.com/crawlab-team/crawlab/core/interfaces" "github.com/crawlab-team/crawlab/core/node/service" "github.com/crawlab-team/crawlab/core/utils" "github.com/spf13/viper" - "net/http" - _ "net/http/pprof" - "sync" ) type Server struct { diff --git a/core/controllers/base.go b/core/controllers/base.go index d8c4dd05..3b06c103 100644 --- a/core/controllers/base.go +++ b/core/controllers/base.go @@ -1,11 +1,12 @@ package controllers import ( - "errors" + "github.com/crawlab-team/crawlab/core/entity" "github.com/crawlab-team/crawlab/core/interfaces" "github.com/crawlab-team/crawlab/core/models/service" "github.com/crawlab-team/crawlab/core/mongo" "github.com/gin-gonic/gin" + "github.com/juju/errors" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" mongo2 "go.mongodb.org/mongo-driver/mongo" @@ -14,7 +15,7 @@ import ( type Action struct { Method string Path string - HandlerFunc gin.HandlerFunc + HandlerFunc interface{} } type BaseController[T any] struct { @@ -22,39 +23,68 @@ type BaseController[T any] struct { actions []Action } -func (ctr *BaseController[T]) GetById(c *gin.Context) { - id, err := primitive.ObjectIDFromHex(c.Param("id")) +type GetByIdParams struct { + Id string `path:"id" description:"The ID of the item to get"` +} + +// GetAllParams represents parameters for GetAll method +type GetAllParams struct { + Query bson.M `json:"query"` + Sort bson.D `json:"sort"` +} + +// GetListParams represents parameters for GetList with pagination +type GetListParams struct { + Query bson.M `json:"query"` + Sort bson.D `json:"sort"` + Pagination *entity.Pagination `json:"pagination"` + All bool `query:"all" description:"Whether to get all items"` +} + +func (ctr *BaseController[T]) GetById(_ *gin.Context, params *GetByIdParams) (response *Response[T], err error) { + id, err := primitive.ObjectIDFromHex(params.Id) if err != nil { - HandleErrorBadRequest(c, err) - return + return nil, errors.BadRequestf("invalid id: %s", params.Id) } model, err := ctr.modelSvc.GetById(id) if err != nil { - HandleErrorInternalServerError(c, err) - return + return nil, err } - HandleSuccessWithData(c, model) + return GetSuccessDataResponse(*model) } -func (ctr *BaseController[T]) GetList(c *gin.Context) { +func (ctr *BaseController[T]) GetList(c *gin.Context, params *GetListParams) (response *ListResponse[T], err error) { // get all if query field "all" is set true - all := MustGetFilterAll(c) + all := params.All || MustGetFilterAll(c) + + // Prepare parameters + query := MustGetFilterQuery(c) + sort := MustGetSortOption(c) + if all { - ctr.getAll(c) - return + allParams := &GetAllParams{ + Query: query, + Sort: sort, + } + return ctr.GetAll(c, allParams) } - // get list - ctr.getList(c) + // get list with pagination + pagination := MustGetPagination(c) + listParams := &GetListParams{ + Query: query, + Sort: sort, + Pagination: pagination, + } + return ctr.GetWithPagination(c, listParams) } -func (ctr *BaseController[T]) Post(c *gin.Context) { +func (ctr *BaseController[T]) Post(c *gin.Context) (response *Response[T], err error) { var model T if err := c.ShouldBindJSON(&model); err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorDataResponse[T](err) } u := GetUserFromContext(c) m := any(&model).(interfaces.Model) @@ -64,30 +94,26 @@ func (ctr *BaseController[T]) Post(c *gin.Context) { col := ctr.modelSvc.GetCol() res, err := col.GetCollection().InsertOne(col.GetContext(), m) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorDataResponse[T](err) } result, err := ctr.modelSvc.GetById(res.InsertedID.(primitive.ObjectID)) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorDataResponse[T](err) } - HandleSuccessWithData(c, result) + return GetSuccessDataResponse(*result) } -func (ctr *BaseController[T]) PutById(c *gin.Context) { +func (ctr *BaseController[T]) PutById(c *gin.Context) (response *Response[T], err error) { id, err := primitive.ObjectIDFromHex(c.Param("id")) if err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorDataResponse[T](err) } var model T if err := c.ShouldBindJSON(&model); err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorDataResponse[T](err) } u := GetUserFromContext(c) @@ -95,20 +121,18 @@ func (ctr *BaseController[T]) PutById(c *gin.Context) { m.SetUpdated(u.Id) if err := ctr.modelSvc.ReplaceById(id, model); err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorDataResponse[T](err) } result, err := ctr.modelSvc.GetById(id) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorDataResponse[T](err) } - HandleSuccessWithData(c, result) + return GetSuccessDataResponse(*result) } -func (ctr *BaseController[T]) PatchList(c *gin.Context) { +func (ctr *BaseController[T]) PatchList(c *gin.Context) (res *Response[T], err error) { type Payload struct { Ids []primitive.ObjectID `json:"ids"` Update bson.M `json:"update"` @@ -116,8 +140,7 @@ func (ctr *BaseController[T]) PatchList(c *gin.Context) { var payload Payload if err := c.ShouldBindJSON(&payload); err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorDataResponse[T](err) } // query @@ -129,45 +152,43 @@ func (ctr *BaseController[T]) PatchList(c *gin.Context) { // update if err := ctr.modelSvc.UpdateMany(query, bson.M{"$set": payload.Update}); err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorDataResponse[T](err) } - HandleSuccess(c) + // Return an empty response with success status + var emptyModel T + return GetSuccessDataResponse(emptyModel) } -func (ctr *BaseController[T]) DeleteById(c *gin.Context) { +func (ctr *BaseController[T]) DeleteById(c *gin.Context) (res *Response[T], err error) { id, err := primitive.ObjectIDFromHex(c.Param("id")) if err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorDataResponse[T](err) } if err := ctr.modelSvc.DeleteById(id); err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorDataResponse[T](err) } - HandleSuccess(c) + var emptyModel T + return GetSuccessDataResponse(emptyModel) } -func (ctr *BaseController[T]) DeleteList(c *gin.Context) { +func (ctr *BaseController[T]) DeleteList(c *gin.Context) (res *Response[T], err error) { type Payload struct { Ids []string `json:"ids"` } var payload Payload if err := c.ShouldBindJSON(&payload); err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorDataResponse[T](err) } var ids []primitive.ObjectID for _, id := range payload.Ids { objectId, err := primitive.ObjectIDFromHex(id) if err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorDataResponse[T](err) } ids = append(ids, objectId) } @@ -177,39 +198,46 @@ func (ctr *BaseController[T]) DeleteList(c *gin.Context) { "$in": ids, }, }); err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorDataResponse[T](err) } - HandleSuccess(c) + var emptyModel T + return GetSuccessDataResponse(emptyModel) } -func (ctr *BaseController[T]) getAll(c *gin.Context) { - query := MustGetFilterQuery(c) - sort := MustGetSortOption(c) +// GetAll retrieves all items based on filter and sort +func (ctr *BaseController[T]) GetAll(_ *gin.Context, params *GetAllParams) (response *ListResponse[T], err error) { + query := params.Query + sort := params.Sort if sort == nil { sort = bson.D{{"_id", -1}} } + models, err := ctr.modelSvc.GetMany(query, &mongo.FindOptions{ Sort: sort, }) if err != nil { - HandleErrorInternalServerError(c, err) - return + return nil, err } + total, err := ctr.modelSvc.Count(query) if err != nil { - HandleErrorInternalServerError(c, err) - return + return nil, err } - HandleSuccessWithListData(c, models, total) + + return GetSuccessListResponse(models, total) } -func (ctr *BaseController[T]) getList(c *gin.Context) { +// GetWithPagination retrieves items with pagination +func (ctr *BaseController[T]) GetWithPagination(_ *gin.Context, params *GetListParams) (response *ListResponse[T], err error) { // params - pagination := MustGetPagination(c) - query := MustGetFilterQuery(c) - sort := MustGetSortOption(c) + pagination := params.Pagination + query := params.Query + sort := params.Sort + + if pagination == nil { + pagination = GetDefaultPagination() + } // get list models, err := ctr.modelSvc.GetMany(query, &mongo.FindOptions{ @@ -219,22 +247,49 @@ func (ctr *BaseController[T]) getList(c *gin.Context) { }) if err != nil { if errors.Is(err, mongo2.ErrNoDocuments) { - HandleSuccessWithListData(c, nil, 0) + return GetSuccessListResponse[T](nil, 0) } else { - HandleErrorInternalServerError(c, err) + return nil, err } - return } // total count total, err := ctr.modelSvc.Count(query) if err != nil { - HandleErrorInternalServerError(c, err) - return + return nil, err } // response - HandleSuccessWithListData(c, models, total) + return GetSuccessListResponse(models, total) +} + +// getAll is kept for backward compatibility +func (ctr *BaseController[T]) getAll(c *gin.Context) (response *ListResponse[T], err error) { + query := MustGetFilterQuery(c) + sort := MustGetSortOption(c) + + params := &GetAllParams{ + Query: query, + Sort: sort, + } + + return ctr.GetAll(c, params) +} + +// getList is kept for backward compatibility +func (ctr *BaseController[T]) getList(c *gin.Context) (response *ListResponse[T], err error) { + // params + pagination := MustGetPagination(c) + query := MustGetFilterQuery(c) + sort := MustGetSortOption(c) + + params := &GetListParams{ + Query: query, + Sort: sort, + Pagination: pagination, + } + + return ctr.GetWithPagination(c, params) } func NewController[T any](actions ...Action) *BaseController[T] { diff --git a/core/controllers/http.go b/core/controllers/http.go deleted file mode 100644 index 8aa5169b..00000000 --- a/core/controllers/http.go +++ /dev/null @@ -1,16 +0,0 @@ -package controllers - -type Response[T any] struct { - Status string `json:"status"` - Message string `json:"message"` - Data T `json:"data"` - Error string `json:"error"` -} - -type ListResponse[T any] struct { - Status string `json:"status"` - Message string `json:"message"` - Total int `json:"total"` - Data []T `json:"data"` - Error string `json:"error"` -} diff --git a/core/controllers/openapi.go b/core/controllers/openapi.go new file mode 100644 index 00000000..d6996b7c --- /dev/null +++ b/core/controllers/openapi.go @@ -0,0 +1,16 @@ +package controllers + +import ( + "github.com/gin-gonic/gin" + "github.com/wI2L/fizz/openapi" +) + +func GetOpenAPI(c *gin.Context) { + info := &openapi.Info{ + Title: "Crawlab API", + Description: "REST API for Crawlab", + Version: "0.7.0", + } + handleFunc := globalWrapper.GetFizz().OpenAPI(info, "json") + handleFunc(c) +} diff --git a/core/controllers/router.go b/core/controllers/router.go index 42510dc1..9fa346fd 100644 --- a/core/controllers/router.go +++ b/core/controllers/router.go @@ -2,25 +2,40 @@ package controllers import ( "net/http" + "strings" "github.com/crawlab-team/crawlab/core/middlewares" "github.com/crawlab-team/crawlab/core/models/models" + "github.com/crawlab-team/crawlab/core/openapi" "github.com/gin-gonic/gin" ) // RouterGroups defines the different authentication levels for API routes type RouterGroups struct { - AuthGroup *gin.RouterGroup // Routes requiring full authentication - SyncAuthGroup *gin.RouterGroup // Routes for sync operations with special auth - AnonymousGroup *gin.RouterGroup // Public routes that don't require auth + AuthGroup *gin.RouterGroup // Routes requiring full authentication + SyncAuthGroup *gin.RouterGroup // Routes for sync operations with special auth + AnonymousGroup *gin.RouterGroup // Public routes that don't require auth + Wrapper *openapi.FizzWrapper // OpenAPI wrapper for documentation +} + +// Global variable to store the OpenAPI wrapper +// This is a workaround since we can't easily pass it through the Gin context +var globalWrapper *openapi.FizzWrapper + +func GetGlobalFizzWrapper() *openapi.FizzWrapper { + return globalWrapper } // NewRouterGroups initializes the router groups with their respective middleware func NewRouterGroups(app *gin.Engine) (groups *RouterGroups) { + // Create OpenAPI wrapper + wrapper := openapi.NewFizzWrapper(app) + return &RouterGroups{ AuthGroup: app.Group("/", middlewares.AuthorizationMiddleware()), SyncAuthGroup: app.Group("/", middlewares.SyncAuthorizationMiddleware()), AnonymousGroup: app.Group("/"), + Wrapper: wrapper, } } @@ -30,36 +45,170 @@ func RegisterController[T any](group *gin.RouterGroup, basePath string, ctr *Bas // Track registered paths to avoid duplicates actionPaths := make(map[string]bool) for _, action := range ctr.actions { - group.Handle(action.Method, basePath+action.Path, action.HandlerFunc) path := basePath + action.Path key := action.Method + " - " + path actionPaths[key] = true + + // Create appropriate model response based on the action + responses := globalWrapper.BuildModelResponse() + + id := getIDForAction(action.Method, path) + summary := getSummaryForAction(action.Method, basePath, action.Path) + description := getDescriptionForAction(action.Method, basePath, action.Path) + + globalWrapper.RegisterRoute(action.Method, path, action.HandlerFunc, id, summary, description, responses) } - registerBuiltinHandler(group, http.MethodGet, basePath+"", ctr.GetList, actionPaths) - registerBuiltinHandler(group, http.MethodGet, basePath+"/:id", ctr.GetById, actionPaths) - registerBuiltinHandler(group, http.MethodPost, basePath+"", ctr.Post, actionPaths) - registerBuiltinHandler(group, http.MethodPut, basePath+"/:id", ctr.PutById, actionPaths) - registerBuiltinHandler(group, http.MethodPatch, basePath+"", ctr.PatchList, actionPaths) - registerBuiltinHandler(group, http.MethodDelete, basePath+"/:id", ctr.DeleteById, actionPaths) - registerBuiltinHandler(group, http.MethodDelete, basePath+"", ctr.DeleteList, actionPaths) + + // Register built-in handlers if they haven't been overridden + // Create a zero value of T to use as the model + var model T + registerBuiltinHandler(group, globalWrapper, basePath, http.MethodGet, "", ctr.GetList, actionPaths, "Get list", "Get a list of items", model) + registerBuiltinHandler(group, globalWrapper, basePath, http.MethodGet, "/:id", ctr.GetById, actionPaths, "Get by ID", "Get a single item by ID", model) + registerBuiltinHandler(group, globalWrapper, basePath, http.MethodPost, "", ctr.Post, actionPaths, "Create", "Create a new item", model) + registerBuiltinHandler(group, globalWrapper, basePath, http.MethodPut, "/:id", ctr.PutById, actionPaths, "Update by ID", "Update an item by ID", model) + registerBuiltinHandler(group, globalWrapper, basePath, http.MethodPatch, "", ctr.PatchList, actionPaths, "Patch list", "Patch multiple items", model) + registerBuiltinHandler(group, globalWrapper, basePath, http.MethodDelete, "/:id", ctr.DeleteById, actionPaths, "Delete by ID", "Delete an item by ID", model) + registerBuiltinHandler(group, globalWrapper, basePath, http.MethodDelete, "", ctr.DeleteList, actionPaths, "Delete list", "Delete multiple items", model) } // RegisterActions registers a list of custom action handlers to a route group func RegisterActions(group *gin.RouterGroup, basePath string, actions []Action) { for _, action := range actions { - group.Handle(action.Method, basePath+action.Path, action.HandlerFunc) + path := basePath + action.Path + + // Create generic response + responses := globalWrapper.BuildModelResponse() + + id := getIDForAction(action.Method, path) + summary := getSummaryForAction(action.Method, basePath, action.Path) + description := getDescriptionForAction(action.Method, basePath, action.Path) + + globalWrapper.RegisterRoute(action.Method, path, action.HandlerFunc, id, summary, description, responses) } } // registerBuiltinHandler registers a standard handler if it hasn't been overridden // by a custom action -func registerBuiltinHandler(group *gin.RouterGroup, method, path string, handlerFunc gin.HandlerFunc, existingActionPaths map[string]bool) { +func registerBuiltinHandler[T any](group *gin.RouterGroup, wrapper *openapi.FizzWrapper, basePath, method, pathSuffix string, handlerFunc interface{}, existingActionPaths map[string]bool, summary, description string, model T) { + path := basePath + pathSuffix key := method + " - " + path _, ok := existingActionPaths[key] if ok { return } - group.Handle(method, path, handlerFunc) + + id := getIDForAction(method, path) + + // Create appropriate response based on the method + responses := wrapper.BuildModelResponse() + + wrapper.RegisterRoute(method, path, handlerFunc, id, summary, description, responses) +} + +// Helper functions to generate OpenAPI documentation +func getIDForAction(method, path string) string { + // Remove leading slash and convert remaining slashes to underscores + cleanPath := strings.TrimPrefix(path, "/") + cleanPath = strings.ReplaceAll(cleanPath, "/", "_") + cleanPath = strings.ReplaceAll(cleanPath, ":", "_") + cleanPath = strings.ReplaceAll(cleanPath, "__", "_") + + return method + "_" + cleanPath +} + +func getSummaryForAction(method, basePath, path string) string { + resource := getResourceName(basePath) + + switch method { + case http.MethodGet: + if path == "" { + return "List " + resource + } else if path == "/:id" { + return "Get " + resource + " by ID" + } + case http.MethodPost: + if path == "" { + return "Create " + resource + } + case http.MethodPut: + if path == "/:id" { + return "Update " + resource + " by ID" + } + case http.MethodPatch: + if path == "" { + return "Patch " + resource + " list" + } + case http.MethodDelete: + if path == "/:id" { + return "Delete " + resource + " by ID" + } else if path == "" { + return "Delete " + resource + " list" + } + } + + // For custom actions, use a more descriptive summary + if path != "" && path != "/:id" { + return method + " " + resource + path + } + + return method + " " + resource +} + +func getDescriptionForAction(method, basePath, path string) string { + resource := getResourceName(basePath) + + switch method { + case http.MethodGet: + if path == "" { + return "Get a list of " + resource + " items" + } else if path == "/:id" { + return "Get a single " + resource + " by ID" + } + case http.MethodPost: + if path == "" { + return "Create a new " + resource + } + case http.MethodPut: + if path == "/:id" { + return "Update a " + resource + " by ID" + } + case http.MethodPatch: + if path == "" { + return "Patch multiple " + resource + " items" + } + case http.MethodDelete: + if path == "/:id" { + return "Delete a " + resource + " by ID" + } else if path == "" { + return "Delete multiple " + resource + " items" + } + } + + // For custom actions, use a more descriptive description + if path != "" && path != "/:id" { + return "Perform " + method + " operation on " + resource + " with path " + path + } + + return "Perform " + method + " operation on " + resource +} + +func getResourceName(basePath string) string { + // Remove leading slash and get the last part of the path + if len(basePath) > 0 && basePath[0] == '/' { + basePath = basePath[1:] + } + + // Remove trailing slash if present + if len(basePath) > 0 && basePath[len(basePath)-1] == '/' { + basePath = basePath[:len(basePath)-1] + } + + // If path is empty, return "resource" + if basePath == "" { + return "resource" + } + + return basePath } // InitRoutes configures all API routes for the application @@ -67,6 +216,9 @@ func InitRoutes(app *gin.Engine) (err error) { // Initialize route groups with different auth levels groups := NewRouterGroups(app) + // Store the wrapper in the global variable for later use + globalWrapper = groups.Wrapper + // Register resource controllers with their respective endpoints // Each RegisterController call sets up standard CRUD operations // Additional custom actions can be specified in the controller initialization @@ -418,5 +570,8 @@ func InitRoutes(app *gin.Engine) (err error) { }, }) + // Register OpenAPI documentation route + groups.AnonymousGroup.GET("/openapi.json", GetOpenAPI) + return nil } diff --git a/core/controllers/spider.go b/core/controllers/spider.go index 7392a47e..07aeb47c 100644 --- a/core/controllers/spider.go +++ b/core/controllers/spider.go @@ -74,7 +74,7 @@ func GetSpiderById(c *gin.Context) { HandleSuccessWithData(c, s) } -func GetSpiderList(c *gin.Context) { +func GetSpiderList(c *gin.Context, params *GetListParams) { // get all list all := MustGetFilterAll(c) if all { @@ -85,7 +85,7 @@ func GetSpiderList(c *gin.Context) { // get list withStats := c.Query("stats") if withStats == "" { - NewController[models.Spider]().GetList(c) + NewController[models.Spider]().GetList(c, params) return } diff --git a/core/controllers/task.go b/core/controllers/task.go index af110dbe..e341841f 100644 --- a/core/controllers/task.go +++ b/core/controllers/task.go @@ -67,10 +67,10 @@ func GetTaskById(c *gin.Context) { HandleSuccessWithData(c, t) } -func GetTaskList(c *gin.Context) { +func GetTaskList(c *gin.Context, params *GetListParams) { withStats := c.Query("stats") if withStats == "" { - NewController[models.Task]().GetList(c) + NewController[models.Task]().GetList(c, params) return } diff --git a/core/controllers/utils_http.go b/core/controllers/utils_http.go index 0d50231d..54ac2987 100644 --- a/core/controllers/utils_http.go +++ b/core/controllers/utils_http.go @@ -1,14 +1,55 @@ package controllers import ( + "net/http" + "github.com/crawlab-team/crawlab/core/constants" "github.com/crawlab-team/crawlab/core/entity" "github.com/crawlab-team/crawlab/core/utils" "github.com/crawlab-team/crawlab/trace" "github.com/gin-gonic/gin" - "net/http" ) +type Response[T any] struct { + Status string `json:"status"` + Message string `json:"message"` + Data T `json:"data"` + Error string `json:"error"` +} + +type ListResponse[T any] struct { + Status string `json:"status"` + Message string `json:"message"` + Total int `json:"total"` + Data []T `json:"data"` + Error string `json:"error"` +} + +func GetSuccessDataResponse[T any](model T) (res *Response[T], err error) { + return &Response[T]{ + Status: constants.HttpResponseStatusOk, + Message: constants.HttpResponseMessageSuccess, + Data: model, + }, nil +} + +func GetSuccessListResponse[T any](models []T, total int) (res *ListResponse[T], err error) { + return &ListResponse[T]{ + Status: constants.HttpResponseStatusOk, + Message: constants.HttpResponseMessageSuccess, + Data: models, + Total: total, + }, nil +} + +func GetErrorDataResponse[T any](err error) (res *Response[T], err2 error) { + return &Response[T]{ + Status: constants.HttpResponseStatusOk, + Message: constants.HttpResponseMessageError, + Error: err.Error(), + }, err +} + func handleError(statusCode int, c *gin.Context, err error) { if utils.IsDev() { trace.PrintError(err) diff --git a/core/go.mod b/core/go.mod index ddffb649..c44dee01 100644 --- a/core/go.mod +++ b/core/go.mod @@ -1,6 +1,6 @@ module github.com/crawlab-team/crawlab/core -go 1.22.9 +go 1.23 replace ( github.com/crawlab-team/crawlab/grpc => ../grpc @@ -26,6 +26,7 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-uuid v1.0.3 + github.com/juju/errors v1.0.0 github.com/mitchellh/go-homedir v1.1.0 github.com/pkg/errors v0.9.1 github.com/robfig/cron/v3 v3.0.0 @@ -33,8 +34,10 @@ require ( github.com/spf13/cobra v1.3.0 github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 + github.com/wI2L/fizz v0.22.0 go.mongodb.org/mongo-driver v1.15.1 golang.org/x/oauth2 v0.23.0 + golang.org/x/text v0.21.0 google.golang.org/api v0.189.0 google.golang.org/grpc v1.69.2 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df @@ -72,6 +75,7 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/gofrs/uuid v3.2.0+incompatible // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/s2a-go v0.1.8 // indirect @@ -86,6 +90,7 @@ require ( github.com/klauspost/compress v1.17.7 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect + github.com/loopfz/gadgeto v0.9.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -128,10 +133,10 @@ require ( golang.org/x/net v0.33.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 // indirect google.golang.org/protobuf v1.36.1 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect + gopkg.in/go-playground/validator.v9 v9.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/core/go.sum b/core/go.sum index bbea437e..0c7649e5 100644 --- a/core/go.sum +++ b/core/go.sum @@ -61,6 +61,8 @@ github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Pallinder/go-randomdata v1.2.0 h1:DZ41wBchNRb/0GfsePLiSwb0PHZmT67XY00lCDlaYPg= +github.com/Pallinder/go-randomdata v1.2.0/go.mod h1:yHmJgulpD2Nfrm0cR9tI/+oAgRqCQQixsA8HyRZfV9Y= github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE= @@ -134,6 +136,7 @@ github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -169,8 +172,14 @@ github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyT 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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/cors v1.3.0/go.mod h1:artPvLlhkF7oG06nK8v3U8TNz6IeX+w1uzCSEId5/Vc= +github.com/gin-contrib/sse v0.0.0-20190125020943-a7658810eb74/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= 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.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= 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/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= @@ -197,18 +206,29 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 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-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 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.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= 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.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= 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/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 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/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= @@ -290,6 +310,7 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -361,6 +382,9 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/juju/errors v0.0.0-20190930114154-d42613fe1ab9/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -377,14 +401,21 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 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.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +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/loopfz/gadgeto v0.9.0 h1:yrQVBgdGhAWOB+JjH98sJzYfSqpcpKzOBIEtJFcRl2s= +github.com/loopfz/gadgeto v0.9.0/go.mod h1:S3tK5SXmKY3l39rUpPZw1B/iiy1CftV13QABFhj32Ss= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -397,7 +428,9 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= @@ -440,6 +473,7 @@ github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6 github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -467,6 +501,8 @@ github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -542,8 +578,17 @@ github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9f github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= 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 v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= +github.com/ugorji/go/codec v0.0.0-20190128213124-ee1426cffec0/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= 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/wI2L/fizz v0.22.0 h1:mgRA+uUdESvgsIeBFkMSS/MEIQ4EZ4I2xyRxnCqkhJY= +github.com/wI2L/fizz v0.22.0/go.mod h1:CMxMR1amz8id9wr2YUpONf+F/F9hW1cqRXxVNNuWVxE= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= @@ -612,8 +657,10 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= @@ -811,6 +858,7 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1074,11 +1122,18 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/go-playground/validator.v9 v9.26.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/go-playground/validator.v9 v9.30.0 h1:Wk0Z37oBmKj9/n+tPyBHZmeL19LaCoK3Qq48VwYENss= +gopkg.in/go-playground/validator.v9 v9.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= @@ -1088,6 +1143,7 @@ gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/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= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/core/notification/im.go b/core/notification/im.go index 48020a9a..ea9003bf 100644 --- a/core/notification/im.go +++ b/core/notification/im.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "github.com/apex/log" - "github.com/crawlab-team/crawlab/core/entity" "github.com/crawlab-team/crawlab/core/utils" "io" "net/http" @@ -23,7 +22,7 @@ type ResBody struct { } // RequestParam represents parameters for HTTP requests -type RequestParam entity.RequestParam +type RequestParam map[string]interface{} // performRequest performs an HTTP request with JSON body func performRequest(method, url string, data interface{}) (*http.Response, []byte, error) { diff --git a/core/openapi/wrapper.go b/core/openapi/wrapper.go new file mode 100644 index 00000000..e8259ba9 --- /dev/null +++ b/core/openapi/wrapper.go @@ -0,0 +1,116 @@ +package openapi + +import ( + "fmt" + + "github.com/loopfz/gadgeto/tonic" + + "github.com/gin-gonic/gin" + "github.com/wI2L/fizz" +) + +// FizzWrapper wraps an existing Gin Engine to add OpenAPI functionality +type FizzWrapper struct { + fizz *fizz.Fizz + gin *gin.Engine +} + +// NewFizzWrapper creates a new wrapper around an existing Gin Engine +// This approach ensures we don't break existing functionality +func NewFizzWrapper(engine *gin.Engine) *FizzWrapper { + // Create a new Fizz instance using the existing Gin engine + f := fizz.NewFromEngine(engine) + + return &FizzWrapper{ + fizz: f, + gin: engine, + } +} + +// GetFizz returns the underlying Fizz instance +func (w *FizzWrapper) GetFizz() *fizz.Fizz { + return w.fizz +} + +// GetGin returns the underlying Gin engine +func (w *FizzWrapper) GetGin() *gin.Engine { + return w.gin +} + +// Response represents an OpenAPI response +type Response struct { + Description string + Model interface{} +} + +// RegisterRoute registers a route with OpenAPI documentation +func (w *FizzWrapper) RegisterRoute(method, path string, handler interface{}, id, summary, description string, responses map[int]Response) { + // Build operation options for OpenAPI documentation + opts := w.buildOperationOptions(id, summary, description, responses) + + // Register the route with OpenAPI documentation + switch method { + case "GET": + w.fizz.GET(path, opts, tonic.Handler(handler, 200)) + case "POST": + w.fizz.POST(path, opts, tonic.Handler(handler, 200)) + case "PUT": + w.fizz.PUT(path, opts, tonic.Handler(handler, 200)) + case "DELETE": + w.fizz.DELETE(path, opts, tonic.Handler(handler, 200)) + case "PATCH": + w.fizz.PATCH(path, opts, tonic.Handler(handler, 200)) + case "HEAD": + w.fizz.HEAD(path, opts, tonic.Handler(handler, 200)) + case "OPTIONS": + w.fizz.OPTIONS(path, opts, tonic.Handler(handler, 200)) + } +} + +// BuildModelResponse builds a standard response model with a specific data type +func (w *FizzWrapper) BuildModelResponse() map[int]Response { + return map[int]Response{ + 400: { + Description: "Bad Request", + }, + 401: { + Description: "Unauthorized", + }, + 500: { + Description: "Internal Server Error", + }, + } +} + +// buildOperationOptions builds the options for a Fizz operation +func (w *FizzWrapper) buildOperationOptions(id, summary, description string, responses map[int]Response) []fizz.OperationOption { + var opts []fizz.OperationOption + + // Add ID + if id != "" { + opts = append(opts, fizz.ID(id)) + } + + // Add summary + if summary != "" { + opts = append(opts, fizz.Summary(summary)) + } + + // Add description + if description != "" { + opts = append(opts, fizz.Description(description)) + } + + // Add responses + if responses != nil { + for status, response := range responses { + if response.Model != nil { + opts = append(opts, fizz.Response(fmt.Sprintf("%d", status), response.Description, response.Model, nil, nil)) + } else { + opts = append(opts, fizz.Response(fmt.Sprintf("%d", status), response.Description, nil, nil, nil)) + } + } + } + + return opts +} diff --git a/go.work b/go.work index 91441d84..ea0e5963 100644 --- a/go.work +++ b/go.work @@ -1,4 +1,4 @@ -go 1.22.9 +go 1.23 use ( backend diff --git a/go.work.sum b/go.work.sum index 12ce343b..001314c3 100644 --- a/go.work.sum +++ b/go.work.sum @@ -821,7 +821,6 @@ github.com/crawlab-team/crawlab/template-parser v0.0.0-20240614095218-7b4ee8399a github.com/crawlab-team/crawlab/template-parser v0.0.0-20241016121324-eb56598d93dc/go.mod h1:lKaowLGJrCwvFJnW/KYbdz0Pj69My34yus6IsLKDMro= github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -844,6 +843,7 @@ github.com/fasthttp/websocket v1.4.3-rc.6/go.mod h1:43W9OM2T8FeXpCWMsBd9Cb7nE2CA github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/gin-contrib/cors v1.3.0 h1:PolezCc89peu+NgkIWt9OB01Kbzt6IP0J/JvkG6xxlg= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df h1:Bao6dhmbTA1KFVxmJ6nBoMuOJit2yjEgLJpIMYpop0E= @@ -972,6 +972,11 @@ github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhB github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 h1:K//n/AqR5HjG3qxbrBCL4vJPW0MVFSs9CPK1OOJdRME= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= +github.com/juju/errors v0.0.0-20190930114154-d42613fe1ab9 h1:hJix6idebFclqlfZCHE7EUX7uqLCyb70nHNHH1XKGBg= +github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM= +github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5Qe8= +github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 h1:UUHMLvzt/31azWTN/ifGWef4WUqvXk0iRqdhdy/2uzI= +github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2 h1:Pp8RxiF4rSoXP9SED26WCfNB28/dwTDpPXS8XMJR8rc= github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= @@ -982,7 +987,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGi github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= github.com/linxGnu/gumble v1.0.0 h1:OAJud8Hy4rmV9I5p/KTRiVpwwklMTd9Ankza3Mz7a4M= @@ -1027,7 +1031,6 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7 h1:+/+DxvQaYifJ+grD4klzrS5y+KJXldn/2YTl5JG+vZ8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/sftp v1.10.1 h1:VasscCm72135zRysgrJDKsntdmPN+OuU3+nnHYA9wyc= github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= @@ -1090,6 +1093,7 @@ github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b h1:m74UWYy+HBs+jMFR9 github.com/tj/go-spin v1.1.0 h1:lhdWZsvImxvZ3q1C5OIB7d72DuOwP4O2NdBg9PyzNds= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= +github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E= github.com/valyala/fastrand v1.0.0 h1:LUKT9aKer2dVQNUi3waewTbKV+7H17kvWFNKs2ObdkI= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= @@ -1374,10 +1378,11 @@ google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojt gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec h1:RlWgLqCMMIYYEVcAR5MDsuHlVkaIPDAF+5Dehzg8L5A= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= modernc.org/b v1.0.2 h1:iPC2u39ebzq12GOC2yXT4mve0HrWcH85cz+midWjzeo= modernc.org/db v1.0.3 h1:apxOlWU69je04bY22OT6J0RL23mzvUy22EgTAVyw+Yg=