diff --git a/Dockerfile b/Dockerfile index c8f609b..73284c7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,8 @@ ADD api/ api/ ADD api_handlers/ api_handlers/ +ADD apiresponse/ apiresponse/ + ADD config/ config/ ADD media/ media/ @@ -24,6 +26,8 @@ ADD templates/ templates/ ADD web_handlers/ web_handlers/ +Add webresponse/ webresponse/ + RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o agecem-org . # Alpine diff --git a/api/api.go b/api/api.go index 6ba2d36..9e8fbf7 100644 --- a/api/api.go +++ b/api/api.go @@ -3,147 +3,44 @@ package api import ( "bytes" "encoding/json" - "errors" "fmt" "io" "mime/multipart" "net/http" "net/url" + "codeberg.org/vlbeaudoin/voki" + "git.agecem.com/agecem/agecem-org/apiresponse" "git.agecem.com/agecem/agecem-org/config" - "git.agecem.com/agecem/agecem-org/models" "github.com/spf13/viper" ) type API struct { - Protocol string - Host string - Port int - Opts APIOptions + Voki *voki.Voki } -type APIOptions struct { - KeyAuth bool - Key string - BasicAuth bool - Username string - Password string -} - -// NewApiClientFromViper returns a pointer to a new API object, +// NewFromViper returns a pointer to a new API object, // provided the configuration options are managed by // https://git.agecem.com/agecem/agecem-org/config -func NewApiClientFromViper() (*API, error) { +func NewFromViper(client *http.Client) (api *API, err error) { var config config.Config - - if err := viper.Unmarshal(&config); err != nil { + if err = viper.Unmarshal(&config); err != nil { return nil, err } - api, err := New(config.Server.Api.Protocol, config.Server.Api.Host, config.Server.Api.Port, APIOptions{ - KeyAuth: config.Server.Api.Auth, - Key: config.Server.Api.Key, - }) - if err != nil { - return api, err - } - - return api, nil + return New(client, config.Server.Api.Host, config.Server.Api.Key, config.Server.Api.Port, config.Server.Api.Protocol) } -func New(protocol, host string, port int, opts APIOptions) (*API, error) { - api := API{ - Protocol: protocol, - Host: host, - Port: port, - Opts: opts, - } - - return &api, nil +func New(client *http.Client, host, key string, port int, protocol string) (*API, error) { + return &API{Voki: voki.New(client, host, key, port, protocol)}, nil } -// Call returns a []byte representing a response body. -// Can be used for GET or DELETE methods -func (a *API) Call(method, route string) ([]byte, error) { +func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) (apiresponse.V1DocumentCreateResponse, error) { + var response apiresponse.V1DocumentCreateResponse endpoint := fmt.Sprintf("%s://%s:%d", - a.Protocol, - a.Host, - a.Port, - ) - prerequest := fmt.Sprintf("%s%s", endpoint, route) - request, err := url.QueryUnescape(prerequest) - if err != nil { - return nil, err - } - - switch method { - case http.MethodGet: - // Create client - client := &http.Client{} - - // Create request - request, err := http.NewRequest(http.MethodGet, request, nil) - if err != nil { - return nil, err - } - - if a.Opts.KeyAuth { - request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Opts.Key)) - } - - // Fetch Request - response, err := client.Do(request) - if err != nil { - return nil, err - } - - defer response.Body.Close() - - body, err := io.ReadAll(response.Body) - if err != nil { - return nil, err - } - - return body, nil - case http.MethodDelete: - // Create client - client := &http.Client{} - - // Create request - req, err := http.NewRequest(http.MethodDelete, request, nil) - if err != nil { - return nil, err - } - - if a.Opts.KeyAuth { - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Opts.Key)) - } - - // Fetch Request - resp, err := client.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - // Read Response Body - respBody, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - return respBody, nil - } - - //return nil, errors.New(fmt.Sprintf("method must be 'GET' or 'DELETE', got '%s'", method)) - return nil, errors.New(fmt.Sprintf("method must be 'GET' or 'DELETE', got '%s'", method)) -} - -func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) (models.V1DocumentCreateResponse, error) { - var response models.V1DocumentCreateResponse - endpoint := fmt.Sprintf("%s://%s:%d", - a.Protocol, - a.Host, - a.Port, + a.Voki.Protocol, + a.Voki.Host, + a.Voki.Port, ) current_url := fmt.Sprintf("%s/v1/bucket/%s", endpoint, bucket) @@ -187,8 +84,8 @@ func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) ( req.Header.Set("Content-Type", writer.FormDataContentType()) - if a.Opts.KeyAuth { - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", a.Opts.Key)) + if a.Voki.Key != "" { + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", a.Voki.Key)) } // Send the HTTP request @@ -203,85 +100,6 @@ func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) ( return response, err } -// CallWithData takes data and returns a string representing a response body. -// Can be used for POST or PUT methods -func (a *API) CallWithData(method, route string, data []byte) (string, error) { - endpoint := fmt.Sprintf("%s://%s:%d", - a.Protocol, - a.Host, - a.Port, - ) - request := fmt.Sprintf("%s%s", endpoint, route) - - switch method { - case http.MethodPost: - // initialize http client - client := &http.Client{} - - // set the HTTP method, url, and request body - req, err := http.NewRequest(http.MethodPost, request, bytes.NewBuffer(data)) - if err != nil { - return "", err - } - - if a.Opts.KeyAuth { - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Opts.Key)) - } - - // set the request header Content-Type for json - req.Header.Set("Content-Type", "application/json; charset=utf-8") - resp, err := client.Do(req) - if err != nil { - return "", err - } - - var res map[string]interface{} - - json.NewDecoder(resp.Body).Decode(&res) - return fmt.Sprintf("%s\n", res["message"]), nil - /* - case http.MethodPut: - // initialize http client - client := &http.Client{} - - // set the HTTP method, url, and request body - req, err := http.NewRequest(http.MethodPut, request, bytes.NewBuffer(data)) - if err != nil { - return "", err - } - - if a.Opts.KeyAuth { - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Opts.Key)) - } - - // set the request header Content-Type for json - //req.Header.Set("Content-Type", "application/json; charset=utf-8") - resp, err := client.Do(req) - if err != nil { - return "", err - } - - var res map[string]interface{} - - json.NewDecoder(resp.Body).Decode(&res) - return fmt.Sprintf("%s\n", res["message"]), nil - */ - } - - //return "", errors.New(fmt.Sprintf("method must be 'POST' or 'PUT', got '%s'", method)) - return "", errors.New(fmt.Sprintf("method must be 'POST', got '%s'", method)) -} - -func (a *API) ListBuckets() (models.V1BucketListResponse, error) { - var response models.V1BucketListResponse - result, err := a.Call(http.MethodGet, "/v1/bucket") - if err != nil { - return response, err - } - - if err = json.Unmarshal(result, &response); err != nil { - return response, err - } - - return response, nil +func (a *API) ListBuckets() (response apiresponse.V1BucketListResponse, err error) { + return response, a.Voki.Unmarshal(http.MethodGet, "/v1/bucket", nil, true, &response) } diff --git a/api_handlers/api_handlers.go b/api_handlers/api_handlers.go index 8ce859f..21ddb63 100644 --- a/api_handlers/api_handlers.go +++ b/api_handlers/api_handlers.go @@ -5,9 +5,9 @@ import ( "net/http" "sort" + "git.agecem.com/agecem/agecem-org/apiresponse" "git.agecem.com/agecem/agecem-org/config" "git.agecem.com/agecem/agecem-org/media" - "git.agecem.com/agecem/agecem-org/models" "github.com/labstack/echo/v4" "github.com/minio/minio-go/v7" ) @@ -30,7 +30,7 @@ func (h *V1Handler) HandleV1(c echo.Context) error { // HandleV1Seed créé des buckets dans minio selon la liste de buckets dans server.documents.buckets // Les buckets sont créés avec paramètres par défaut, et sont ensuite visible dans /v1/bucket. func (h *V1Handler) HandleV1Seed(c echo.Context) error { - var response models.V1SeedResponse + var response apiresponse.V1SeedResponse new_buckets, err := h.MediaClient.Seed() response.Data.Buckets = new_buckets @@ -56,7 +56,7 @@ func (h *V1Handler) HandleV1Seed(c echo.Context) error { // HandleV1BucketList affiche les buckets permis par server.documents.buckets, qui existent. func (h *V1Handler) HandleV1BucketList(c echo.Context) error { - var response models.V1BucketListResponse + var response apiresponse.V1BucketListResponse var buckets = make(map[string]string) @@ -83,7 +83,7 @@ func (h *V1Handler) HandleV1BucketList(c echo.Context) error { } func (h *V1Handler) HandleV1BucketRead(c echo.Context) error { - var response models.V1BucketReadResponse + var response apiresponse.V1BucketReadResponse bucket := c.Param("bucket") @@ -95,7 +95,7 @@ func (h *V1Handler) HandleV1BucketRead(c echo.Context) error { } if !allowed { - return c.JSON(models.NotFoundResponse()) + return c.JSON(apiresponse.NotFoundResponse()) } ctx, cancel := context.WithCancel(context.Background()) @@ -112,7 +112,7 @@ func (h *V1Handler) HandleV1BucketRead(c echo.Context) error { } if !exists { - return c.JSON(models.NotFoundResponse()) + return c.JSON(apiresponse.NotFoundResponse()) } objectCh := h.MediaClient.MinioClient.ListObjects(ctx, bucket, minio.ListObjectsOptions{}) @@ -137,7 +137,7 @@ func (h *V1Handler) HandleV1BucketRead(c echo.Context) error { // HandleV1DocumentCreate permet d'ajouter un object dans un bucket, par multipart/form-data func (h *V1Handler) HandleV1DocumentCreate(c echo.Context) error { - var response models.V1DocumentCreateResponse + var response apiresponse.V1DocumentCreateResponse bucket := c.Param("bucket") @@ -158,7 +158,7 @@ func (h *V1Handler) HandleV1DocumentCreate(c echo.Context) error { } if !allowed { - return c.JSON(models.NotFoundResponse()) + return c.JSON(apiresponse.NotFoundResponse()) } ctx, cancel := context.WithCancel(context.Background()) @@ -208,7 +208,7 @@ func (h *V1Handler) HandleV1DocumentRead(c echo.Context) error { } if !allowed { - return c.JSON(models.NotFoundResponse()) + return c.JSON(apiresponse.NotFoundResponse()) } ctx, cancel := context.WithCancel(context.Background()) @@ -221,7 +221,7 @@ func (h *V1Handler) HandleV1DocumentRead(c echo.Context) error { } if !bucket_exists { - return c.JSON(models.NotFoundResponse()) + return c.JSON(apiresponse.NotFoundResponse()) } document_info, err := h.MediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{}) @@ -229,7 +229,7 @@ func (h *V1Handler) HandleV1DocumentRead(c echo.Context) error { if err != nil { if err.Error() == "The specified key does not exist." { - return c.JSON(models.NotFoundResponse()) + return c.JSON(apiresponse.NotFoundResponse()) } return c.JSON(http.StatusInternalServerError, map[string]interface{}{ @@ -253,7 +253,7 @@ func (h *V1Handler) HandleV1DocumentRead(c echo.Context) error { // HandleV1DocumentUpdate permet de mettre à jour certains champs d'un object, comme le Content-Type ou le Filename func (h *V1Handler) HandleV1DocumentUpdate(c echo.Context) error { - return c.JSON(models.NotImplementedResponse()) + return c.JSON(apiresponse.NotImplementedResponse()) } // HandleV1DocumentDelete permet de supprimer un object @@ -269,7 +269,7 @@ func (h *V1Handler) HandleV1DocumentDelete(c echo.Context) error { } if !allowed { - return c.JSON(models.NotFoundResponse()) + return c.JSON(apiresponse.NotFoundResponse()) } ctx, cancel := context.WithCancel(context.Background()) @@ -282,14 +282,14 @@ func (h *V1Handler) HandleV1DocumentDelete(c echo.Context) error { } if !bucket_exists { - return c.JSON(models.NotFoundResponse()) + return c.JSON(apiresponse.NotFoundResponse()) } document_info, err := h.MediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{}) if err != nil { if err.Error() == "The specified key does not exist." { - return c.JSON(models.NotFoundResponse()) + return c.JSON(apiresponse.NotFoundResponse()) } return c.JSON(http.StatusInternalServerError, map[string]interface{}{ diff --git a/apiresponse/apiresponse.go b/apiresponse/apiresponse.go new file mode 100644 index 0000000..d4e98da --- /dev/null +++ b/apiresponse/apiresponse.go @@ -0,0 +1,45 @@ +package apiresponse + +import ( + "net/http" +) + +type Responder interface { + Respond() Responder +} + +type Response struct { + StatusCode int `json:"status_code"` + Message string + Error string +} + +func (r Response) Respond() Responder { + return r +} + +type SimpleResponse struct { + Message string +} + +func (r SimpleResponse) Respond() Responder { + return r +} + +func NotFoundResponse() (int, SimpleResponse) { + return http.StatusNotFound, SimpleResponse{ + Message: "Not Found", + } +} + +func NotImplementedResponse() (int, SimpleResponse) { + return http.StatusNotImplemented, SimpleResponse{ + Message: "Not Implemented", + } +} + +func InternalServerErrorResponse() (int, SimpleResponse) { + return http.StatusInternalServerError, SimpleResponse{ + Message: "Internal Server Error", + } +} diff --git a/apiresponse/bucket.go b/apiresponse/bucket.go new file mode 100644 index 0000000..02c26a3 --- /dev/null +++ b/apiresponse/bucket.go @@ -0,0 +1,15 @@ +package apiresponse + +type V1BucketListResponse struct { + Response + Data struct { + Buckets map[string]string + } +} + +type V1BucketReadResponse struct { + Response + Data struct { + Keys []string + } +} diff --git a/apiresponse/document.go b/apiresponse/document.go new file mode 100644 index 0000000..9e1677b --- /dev/null +++ b/apiresponse/document.go @@ -0,0 +1,10 @@ +package apiresponse + +type V1DocumentCreateResponse struct { + Response + Data struct { + Bucket string + Key string + Size int64 + } +} diff --git a/apiresponse/seed.go b/apiresponse/seed.go new file mode 100644 index 0000000..858beab --- /dev/null +++ b/apiresponse/seed.go @@ -0,0 +1,8 @@ +package apiresponse + +type V1SeedResponse struct { + Response + Data struct { + Buckets []string + } +} diff --git a/cmd/server.go b/cmd/server.go index aac8313..47c61f0 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -13,6 +13,7 @@ import ( "io" "net/http" + "codeberg.org/vlbeaudoin/serpents" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -69,9 +70,9 @@ func init() { publicFS = public.GetPublicFS() templatesFS = templates.GetTemplatesFS() - // server.port - --server-port - serverCmd.Flags().Int("server-port", 8080, "Port to run the webserver on (config: server.port)") - viper.BindPFlag("server.port", serverCmd.Flags().Lookup("server-port")) + serpents.Int(serverCmd.Flags(), + "server.port", "server-port", 8080, + "Port to run the webserver on") // Not currently used /* @@ -80,62 +81,62 @@ func init() { viper.BindPFlag("server.documents.location", serverCmd.Flags().Lookup("server-documents-location")) */ - // server.documents.endpoint - --server-documents-endpoint - serverCmd.Flags().String("server-documents-endpoint", "minio:9000", "Storage server endpoint (config: server.documents.endpoint)") - viper.BindPFlag("server.documents.endpoint", serverCmd.Flags().Lookup("server-documents-endpoint")) + serpents.String(serverCmd.Flags(), + "server.documents.endpoint", "server-documents-endpoint", "minio:9000", + "Storage server endpoint") - // server.documents.access_key_id - --server-documents-access-key-id - serverCmd.Flags().String("server-documents-access-key-id", "agecem-org", "Storage server access key id (config: server.documents.access_key_id)") - viper.BindPFlag("server.documents.access_key_id", serverCmd.Flags().Lookup("server-documents-access-key-id")) + serpents.String(serverCmd.Flags(), + "server.documents.access_key_id", "server-documents-access-key-id", "agecem-org", + "Storage server access key id") - // server.documents.secret_access_key - --server-documents-secret-access-key - serverCmd.Flags().String("server-documents-secret-access-key", "agecem-org", "Storage server secret access key (config: server.documents.secret_access_key)") - viper.BindPFlag("server.documents.secret_access_key", serverCmd.Flags().Lookup("server-documents-secret-access-key")) + serpents.String(serverCmd.Flags(), + "server.documents.secret_access_key", "server-documents-secret-access-key", "agecem-org", + "Storage server secret access key") - // server.documents.use_ssl - --server-documents-use-ssl - serverCmd.Flags().Bool("server-documents-use-ssl", false, "Storage server SSL status (config: server.documents.use_ssl)") - viper.BindPFlag("server.documents.use_ssl", serverCmd.Flags().Lookup("server-documents-use-ssl")) + serpents.Bool(serverCmd.Flags(), + "server.documents.use_ssl", "server-documents-use-ssl", false, + "Storage server SSL status") - // server.documents.buckets - --server-documents-buckets - serverCmd.Flags().StringToString("server-documents-buckets", map[string]string{ - "proces-verbaux": "Procès-verbaux", - "politiques": "Politiques", - "reglements": "Règlements", - "formulaires": "Formulaires", - }, "Buckets that are allowed to be accessed by the API (config: server.documents.buckets)") - viper.BindPFlag("server.documents.buckets", serverCmd.Flags().Lookup("server-documents-buckets")) + serpents.StringToString(serverCmd.Flags(), + "server.documents.buckets", "server-documents-buckets", map[string]string{ + "proces-verbaux": "Procès-verbaux", + "politiques": "Politiques", + "reglements": "Règlements", + "formulaires": "Formulaires", + }, + "Buckets that are allowed to be accessed by the API") - // server.api.auth - --server-api-auth - serverCmd.Flags().Bool("server-api-auth", true, "Enable to allow key authentication for /v1 routes (config: server.api.auth)") - viper.BindPFlag("server.api.auth", serverCmd.Flags().Lookup("server-api-auth")) + serpents.Bool(serverCmd.Flags(), + "server.api.auth", "server-api-auth", true, + "Enable to allow key authentication for /v1 routes") - // server.api.key - --server-api-key - serverCmd.Flags().String("server-api-key", "agecem-org", "Key to use for authenticating to /v1 routes") - viper.BindPFlag("server.api.key", serverCmd.Flags().Lookup("server-api-key")) + serpents.String(serverCmd.Flags(), + "server.api.key", "server-api-key", "agecem-org", + "Key to use for authenticating to /v1 routes") - // server.api.port - serverCmd.Flags().Int("server-api-port", 8080, "API server port (config: server.api.port)") - viper.BindPFlag("server.api.port", serverCmd.Flags().Lookup("server-api-port")) + serpents.Int(serverCmd.Flags(), + "server.api.port", "server-api-port", 8080, + "API server port") - // server.api.protocol - serverCmd.Flags().String("server-api-protocol", "http", "API server protocol (http/https) (config: server.api.protocol)") - viper.BindPFlag("server.api.protocol", serverCmd.Flags().Lookup("server-api-protocol")) + serpents.String(serverCmd.Flags(), + "server.api.protocol", "server-api-protocol", "http", + "API server protocol (http/https)") - // server.api.host - serverCmd.Flags().String("server-api-host", "localhost", "API server host (config: server.api.host)") - viper.BindPFlag("server.api.host", serverCmd.Flags().Lookup("server-api-host")) + serpents.String(serverCmd.Flags(), + "server.api.host", "server-api-host", "localhost", + "API server host") - // server.admin.auth - --server-admin-auth - serverCmd.Flags().Bool("server-admin-auth", true, "Enable to allow basic authentication for /admin routes (config: server.admin.auth)") - viper.BindPFlag("server.admin.auth", serverCmd.Flags().Lookup("server-admin-auth")) + serpents.Bool(serverCmd.Flags(), + "server.admin.auth", "server-admin-auth", true, + "Enable to allow basic authentication for /admin routes") - // server.admin.username - --server-admin-username - serverCmd.Flags().String("server-admin-username", "agecem-org", "Username for basic authentication for /admin routes (config: server.admin.username)") - viper.BindPFlag("server.admin.username", serverCmd.Flags().Lookup("server-admin-username")) + serpents.String(serverCmd.Flags(), + "server.admin.username", "server-admin-username", "agecem-org", + "Username for basic authentication for /admin routes") - // server.admin.password - --server-admin-password - serverCmd.Flags().String("server-admin-password", "agecem-org", "Password for basic authentication for /admin routes (config: server.admin.password)") - viper.BindPFlag("server.admin.password", serverCmd.Flags().Lookup("server-admin-password")) + serpents.String(serverCmd.Flags(), + "server.admin.password", "server-admin-password", "agecem-org", + "Password for basic authentication for /admin routes") } func RunServer() { @@ -226,9 +227,12 @@ func RunServer() { groupV1.DELETE("/bucket/:bucket/:document", v1Handler.HandleV1DocumentDelete) // HTML Routes - apiClient, err := api.NewApiClientFromViper() + client := http.DefaultClient + defer client.CloseIdleConnections() + + apiClient, err := api.NewFromViper(client) if err != nil { - log.Fatal("Error during NewMediaClientFromViper for API handlers") + log.Fatal(err) } webHandler := web_handlers.WebHandler{ diff --git a/go.mod b/go.mod index 07d9b53..8456838 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,14 @@ module git.agecem.com/agecem/agecem-org -go 1.19 +go 1.21.1 require ( + codeberg.org/vlbeaudoin/serpents v1.0.2 + codeberg.org/vlbeaudoin/voki v1.6.0 github.com/labstack/echo/v4 v4.10.0 github.com/minio/minio-go/v7 v7.0.52 github.com/spf13/cobra v1.6.1 - github.com/spf13/viper v1.15.0 + github.com/spf13/viper v1.16.0 ) require ( @@ -28,20 +30,20 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // 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.0.6 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/rs/xid v1.4.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect - github.com/spf13/afero v1.9.3 // indirect - github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect - golang.org/x/crypto v0.6.0 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.2.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 6793237..76d5af0 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,10 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +codeberg.org/vlbeaudoin/serpents v1.0.2 h1:mHuL+RBAMOGeiB5+ew1cRputEAnOIQNJW9o9a5Qjudo= +codeberg.org/vlbeaudoin/serpents v1.0.2/go.mod h1:3bE/R0ToABwcUJtS1VcGEBa86K5FYhrZGAbFl2qL8kQ= +codeberg.org/vlbeaudoin/voki v1.6.0 h1:Mm4K+SK5VmeWKnzUvcAkUxi2zcFQvzGX650Zi/FA7Iw= +codeberg.org/vlbeaudoin/voki v1.6.0/go.mod h1:5XTLx/KiW/OfiupF3o7PAAAU/UhsPdKSrVMmtHbmkPI= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -58,7 +62,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -103,6 +108,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -145,10 +151,12 @@ github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZX github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +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/labstack/echo/v4 v4.10.0 h1:5CiyngihEO4HXsz3vVsJn7f8xAlWwRr3aY6Ih280ZKA= github.com/labstack/echo/v4 v4.10.0/go.mod h1:S/T/5fy/GigaXnHTkh0ZGe4LpkkQysvRjFMSUTkDRNQ= github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= @@ -174,32 +182,33 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w 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/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= -github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +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/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= -github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= -github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= +github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= +github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= 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= @@ -210,8 +219,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 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 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -235,9 +244,9 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U 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-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -302,8 +311,9 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -355,6 +365,7 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -364,8 +375,8 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -373,8 +384,10 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/models/responses.go b/models/responses.go deleted file mode 100644 index dd72936..0000000 --- a/models/responses.go +++ /dev/null @@ -1,92 +0,0 @@ -package models - -import "net/http" - -type Responder interface { - Respond() Responder -} - -type Response struct { - StatusCode int `json:"status_code"` - Message string - Error string -} - -func (r Response) Respond() Responder { - return r -} - -type SimpleResponse struct { - Message string -} - -func (r SimpleResponse) Respond() Responder { - return r -} - -func NotFoundResponse() (int, SimpleResponse) { - return http.StatusNotFound, SimpleResponse{ - Message: "Not Found", - } -} - -func NotImplementedResponse() (int, SimpleResponse) { - return http.StatusNotImplemented, SimpleResponse{ - Message: "Not Implemented", - } -} - -type HandleAdminDocumentsUploadResponse struct { - Response - Data struct { - Buckets []Bucket - } -} - -type HandleDocumentationResponse struct { - Response - Data struct { - Buckets []Bucket - } -} - -type UploadDocumentResponse struct { - Response - Data UploadDocumentResponseData -} - -type UploadDocumentResponseData struct { - Bucket string - Object string - Size float64 -} - -type V1SeedResponse struct { - Response - Data struct { - Buckets []string - } -} - -type V1BucketListResponse struct { - Response - Data struct { - Buckets map[string]string - } -} - -type V1BucketReadResponse struct { - Response - Data struct { - Keys []string - } -} - -type V1DocumentCreateResponse struct { - Response - Data struct { - Bucket string - Key string - Size int64 - } -} diff --git a/web_handlers/web_handlers.go b/web_handlers/web_handlers.go index 1dd96e9..293666e 100644 --- a/web_handlers/web_handlers.go +++ b/web_handlers/web_handlers.go @@ -1,13 +1,16 @@ package web_handlers import ( - "encoding/json" "fmt" + "io" "net/http" + "net/url" "sort" "git.agecem.com/agecem/agecem-org/api" + "git.agecem.com/agecem/agecem-org/apiresponse" "git.agecem.com/agecem/agecem-org/models" + "git.agecem.com/agecem/agecem-org/webresponse" "github.com/labstack/echo/v4" ) @@ -48,13 +51,13 @@ func HandleVieEtudianteOrganisme(c echo.Context) error { } func (h *WebHandler) HandleDocumentation(c echo.Context) error { - var response models.HandleDocumentationResponse + var response webresponse.HandleDocumentationResponse v1BucketListResponse, err := h.ApiClient.ListBuckets() if err != nil { - response.StatusCode = v1BucketListResponse.StatusCode - response.Message = v1BucketListResponse.Message response.Error = err.Error() + response.Message = v1BucketListResponse.Message + response.StatusCode = v1BucketListResponse.StatusCode return c.Render(response.StatusCode, "documentation-html", response) } @@ -63,24 +66,14 @@ func (h *WebHandler) HandleDocumentation(c echo.Context) error { for bucket, displayName := range v1BucketListResponse.Data.Buckets { // TODO move call to dedicated API client method - content, err := h.ApiClient.Call(http.MethodGet, fmt.Sprintf("/v1/bucket/%s", bucket)) - if err != nil { - response.StatusCode = http.StatusInternalServerError - response.Message = "Error during /v1/bucket/:bucket" + var v1BucketReadResponse apiresponse.V1BucketReadResponse + + if err = h.ApiClient.Voki.Unmarshal(http.MethodGet, fmt.Sprintf("/v1/bucket/%s", bucket), nil, true, &v1BucketReadResponse); err != nil { response.Error = err.Error() - - return c.Render(response.StatusCode, "documentation-html", response) - } - - var v1BucketReadResponse models.V1BucketReadResponse - - err = json.Unmarshal(content, &v1BucketReadResponse) - if err != nil { - response.StatusCode = http.StatusInternalServerError response.Message = "Error during json.Unmarshal /v1/bucket/:bucket" - response.Error = err.Error() + response.StatusCode = http.StatusInternalServerError - return c.Render(response.StatusCode, "documentation-html", response) + return c.Render(http.StatusOK, "documentation-html", response) } response.Data.Buckets = append(response.Data.Buckets, models.Bucket{ @@ -96,7 +89,7 @@ func (h *WebHandler) HandleDocumentation(c echo.Context) error { //response.Message = "HandleDocumentation ok" // TODO render .Message - return c.Render(response.StatusCode, "documentation-html", response) + return c.Render(http.StatusOK, "documentation-html", response) //return c.Render(response.StatusCode, "documentation-html", response.Data.Buckets) } @@ -108,20 +101,30 @@ func (h *WebHandler) HandlePublicDocumentation(c echo.Context) error { bucket := c.Param("bucket") document := c.Param("document") - result, err := h.ApiClient.Call(http.MethodGet, fmt.Sprintf("/v1/bucket/%s/%s", bucket, document)) + unescaped, err := url.QueryUnescape(fmt.Sprintf("/v1/bucket/%s/%s", bucket, document)) if err != nil { - return c.JSON(models.NotFoundResponse()) + return c.JSON(http.StatusBadRequest, map[string]string{"message": "Bad Request"}) } - // Check if result can fit inside a map containing a message - var result_map map[string]string + response, err := h.ApiClient.Voki.Call(http.MethodGet, unescaped, nil, true) + if err != nil { + return c.JSON(apiresponse.NotFoundResponse()) + } + defer response.Body.Close() - err = json.Unmarshal(result, &result_map) - if err == nil { - return c.JSON(http.StatusBadRequest, result_map) + switch response.StatusCode { + case http.StatusNotFound: + return c.JSON(apiresponse.NotFoundResponse()) + case http.StatusInternalServerError: + return c.JSON(http.StatusInternalServerError, map[string]string{"message": "Internal Server Error"}) } - return c.Blob(http.StatusOK, "application/octet-stream", result) + body, err := io.ReadAll(response.Body) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{"message": "Internal Server Error"}) + } + + return c.Blob(http.StatusOK, "application/octet-stream", body) } func HandleAdmin(c echo.Context) error { @@ -129,7 +132,7 @@ func HandleAdmin(c echo.Context) error { } func (h *WebHandler) HandleAdminDocumentsUpload(c echo.Context) error { - var response models.HandleAdminDocumentsUploadResponse + var response webresponse.HandleAdminDocumentsUploadResponse v1BucketListResponse, err := h.ApiClient.ListBuckets() if err != nil { @@ -152,7 +155,7 @@ func (h *WebHandler) HandleAdminDocumentsUpload(c echo.Context) error { } func (h *WebHandler) HandleAdminDocumentsUploadPOST(c echo.Context) error { - var response models.HandleAdminDocumentsUploadResponse + var response webresponse.HandleAdminDocumentsUploadResponse v1BucketListResponse, err := h.ApiClient.ListBuckets() if err != nil { diff --git a/webresponse/webresponse.go b/webresponse/webresponse.go new file mode 100644 index 0000000..b17b463 --- /dev/null +++ b/webresponse/webresponse.go @@ -0,0 +1,31 @@ +package webresponse + +import ( + "git.agecem.com/agecem/agecem-org/apiresponse" + "git.agecem.com/agecem/agecem-org/models" +) + +type HandleAdminDocumentsUploadResponse struct { + apiresponse.Response + Data struct { + Buckets []models.Bucket + } +} + +type HandleDocumentationResponse struct { + apiresponse.Response + Data struct { + Buckets []models.Bucket + } +} + +type UploadDocumentResponse struct { + apiresponse.Response + Data UploadDocumentResponseData +} + +type UploadDocumentResponseData struct { + Bucket string + Object string + Size float64 +}