From 2bfa133349307ffe528dde600adca1da8b32a6e8 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Wed, 26 Apr 2023 19:15:22 -0400 Subject: [PATCH 1/7] Ajouter groupe /admin avec basic auth Pour admin panel --- cmd/server.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/cmd/server.go b/cmd/server.go index 71f5a54..724af2a 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -81,6 +81,18 @@ func init() { // server.api.key - --server-api-key serverCmd.Flags().String("server-api-key", "", "Key to use for authenticating to /v1 routes") viper.BindPFlag("server.api.key", serverCmd.Flags().Lookup("server-api-key")) + + // server.admin.auth - --server-admin-auth + serverCmd.Flags().Bool("server-admin-auth", false, "Enable to allow basic authentication for /admin routes (config: server.admin.auth)") + viper.BindPFlag("server.admin.auth", serverCmd.Flags().Lookup("server-admin-auth")) + + // server.admin.username - --server-frontend-username + serverCmd.Flags().String("server-admin-username", "", "Username for basic authentication for /admin routes (config: server.admin.username)") + viper.BindPFlag("server.admin.username", serverCmd.Flags().Lookup("server-admin-username")) + + // server.admin.password - --server-frontend-password + serverCmd.Flags().String("server-admin-password", "", "Password for basic authentication for /admin routes (config: server.admin.password)") + viper.BindPFlag("server.admin.password", serverCmd.Flags().Lookup("server-admin-password")) } func RunServer() { @@ -106,6 +118,35 @@ func RunServer() { groupV1.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) { return subtle.ConstantTimeCompare([]byte(key), []byte(viper.GetString("server.api.key"))) == 1, nil })) + + log.Println("Key auth for /v1 activated") + } + + groupAdmin := e.Group("/admin") + + groupAdmin.Use(middleware.AddTrailingSlash()) + + if viper.GetBool("server.admin.auth") { + username := viper.GetString("server.admin.username") + password := viper.GetString("server.admin.password") + if len(username) < 5 { + log.Fatal("server.admin.auth is enabled, but server.admin.username is too small (needs at least 5 characters)") + } + + if len(password) < 10 { + log.Fatal("server.admin.auth is enabled, but server.admin.password is too small (needs at least 10 characters)") + } + + groupAdmin.Use(middleware.BasicAuth(func(username_entered, password_entered string, c echo.Context) (bool, error) { + // Be careful to use constant time comparison to prevent timing attacks + if subtle.ConstantTimeCompare([]byte(username_entered), []byte(username)) == 1 && + subtle.ConstantTimeCompare([]byte(password_entered), []byte(password)) == 1 { + return true, nil + } + return false, nil + })) + + log.Println("Basic auth for /admin activated") } // API Routes @@ -154,6 +195,10 @@ func RunServer() { e.GET("/public/documentation/:bucket/:document", handlePublicDocumentation) + // Admin Routes + + groupAdmin.GET("", handleAdmin) + e.Logger.Fatal(e.Start( fmt.Sprintf(":%d", viper.GetInt("server.port")))) } @@ -671,6 +716,10 @@ func handlePublicDocumentation(c echo.Context) error { return c.Blob(http.StatusOK, "application/octet-stream", result) } +func handleAdmin(c echo.Context) error { + return c.JSON(http.StatusNotImplemented, map[string]string{"message": "Not Implemented"}) +} + // CSS Handlers func handleStaticCSSIndex(c echo.Context) error { -- 2.45.2 From a1146d182fafead4d3b2401958f37c781f6bf1ee Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Wed, 26 Apr 2023 19:28:20 -0400 Subject: [PATCH 2/7] Ajouter routes /admin et /admin/documents/upload --- cmd/server.go | 8 +++++++- public/html/admin-upload.gohtml | 14 ++++++++++++++ public/html/admin.gohtml | 17 +++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 public/html/admin-upload.gohtml create mode 100644 public/html/admin.gohtml diff --git a/cmd/server.go b/cmd/server.go index 724af2a..906cc2d 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -199,6 +199,8 @@ func RunServer() { groupAdmin.GET("", handleAdmin) + groupAdmin.GET("/documents/upload", handleAdminDocumentsUpload) + e.Logger.Fatal(e.Start( fmt.Sprintf(":%d", viper.GetInt("server.port")))) } @@ -717,7 +719,11 @@ func handlePublicDocumentation(c echo.Context) error { } func handleAdmin(c echo.Context) error { - return c.JSON(http.StatusNotImplemented, map[string]string{"message": "Not Implemented"}) + return c.Render(http.StatusOK, "admin-html", nil) +} + +func handleAdminDocumentsUpload(c echo.Context) error { + return c.Render(http.StatusOK, "admin-upload-html", nil) } // CSS Handlers diff --git a/public/html/admin-upload.gohtml b/public/html/admin-upload.gohtml new file mode 100644 index 0000000..e39f3b3 --- /dev/null +++ b/public/html/admin-upload.gohtml @@ -0,0 +1,14 @@ +{{ define "admin-upload-html" }} + + + + + AGECEM + {{ template "general-html" }} + + + {{ template "header-html" }} +

Upload

+ + +{{ end }} diff --git a/public/html/admin.gohtml b/public/html/admin.gohtml new file mode 100644 index 0000000..d7e0038 --- /dev/null +++ b/public/html/admin.gohtml @@ -0,0 +1,17 @@ +{{ define "admin-html" }} + + + + + AGECEM + {{ template "general-html" }} + + + {{ template "header-html" }} +

Admin

+ + + +{{ end }} -- 2.45.2 From fd3eebb68ccc4a70b4d41ba1f69a43e5265a490e Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Wed, 26 Apr 2023 19:43:43 -0400 Subject: [PATCH 3/7] Ajouter route POST admin documents upload --- cmd/server.go | 12 ++++++++++++ public/html/admin-upload.gohtml | 1 + 2 files changed, 13 insertions(+) diff --git a/cmd/server.go b/cmd/server.go index 906cc2d..9889faf 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -201,6 +201,8 @@ func RunServer() { groupAdmin.GET("/documents/upload", handleAdminDocumentsUpload) + groupAdmin.POST("/documents/upload", handleAdminDocumentsUploadPOST) + e.Logger.Fatal(e.Start( fmt.Sprintf(":%d", viper.GetInt("server.port")))) } @@ -726,6 +728,16 @@ func handleAdminDocumentsUpload(c echo.Context) error { return c.Render(http.StatusOK, "admin-upload-html", nil) } +func handleAdminDocumentsUploadPOST(c echo.Context) error { + data := struct { + Message string + }{ + Message: "Pas implémenté", + } + + return c.Render(http.StatusOK, "admin-upload-html", data) +} + // CSS Handlers func handleStaticCSSIndex(c echo.Context) error { diff --git a/public/html/admin-upload.gohtml b/public/html/admin-upload.gohtml index e39f3b3..7b80e9b 100644 --- a/public/html/admin-upload.gohtml +++ b/public/html/admin-upload.gohtml @@ -9,6 +9,7 @@ {{ template "header-html" }}

Upload

+

{{ .Message }}

{{ end }} -- 2.45.2 From 07005c8753ebfaf114a7c173d518043eab96df36 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Fri, 28 Apr 2023 15:57:09 -0400 Subject: [PATCH 4/7] =?UTF-8?q?WIP:=20Connecter=20/admin/documents/upload?= =?UTF-8?q?=20=C3=A0=20handleV1DocumentCreate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/api.go | 139 +++++++++++++++++++++++++++++++- cmd/server.go | 44 ++++++++-- public/html/admin-upload.gohtml | 13 +++ 3 files changed, 188 insertions(+), 8 deletions(-) diff --git a/api/api.go b/api/api.go index 70334f9..b89346f 100644 --- a/api/api.go +++ b/api/api.go @@ -1,9 +1,13 @@ package api import ( + "bytes" + "encoding/json" "errors" "fmt" "io/ioutil" + "log" + "mime/multipart" "net/http" ) @@ -15,8 +19,11 @@ type API struct { } type APIOptions struct { - KeyAuth bool - Key string + KeyAuth bool + Key string + BasicAuth bool + Username string + Password string } func New(protocol, host string, port int, opts APIOptions) (*API, error) { @@ -97,5 +104,133 @@ func (a *API) Call(method, route string) ([]byte, error) { } 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) (string, error) { + endpoint := fmt.Sprintf("%s://%s:%d", + a.Protocol, + a.Host, + a.Port, + ) + + route := fmt.Sprintf("/v1/bucket/%s", bucket) + + request := fmt.Sprintf("%s%s", endpoint, route) + + client := &http.Client{} + + // set the HTTP method, url, and request body + req, err := http.NewRequest(http.MethodPost, request, nil) + if err != nil { + return "", err + } + + if a.Opts.KeyAuth { + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Opts.Key)) + } + + req.Header.Add("Content-Type", "multipart/form-data") + + // CreateFormFile with multipart.NewWriter + //TODO + /* + buf := new(bytes.Buffer) + form := multipart.NewWriter(buf) + form.CreateFormFile("document", ) + */ + + //TODO + log.Println("req: ", req) + + file, file_header, err := req.FormFile("document") + if err != nil { + log.Println("Error during http#Request.Formfile") + return "", err + } + + log.Println("file: ", file, "file_header: ", file_header) + log.Println("file_header: ", file_header) + + resp, err := client.Do(req) + if err != nil { + log.Println("Error during http#Client.Do") + return "", err + } + + var res map[string]interface{} + + json.NewDecoder(resp.Body).Decode(&res) + return fmt.Sprintf("%s", res), nil +} + +// 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)) +} diff --git a/cmd/server.go b/cmd/server.go index 9889faf..a19fd5d 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -388,7 +388,11 @@ func handleV1DocumentCreate(c echo.Context) error { form_file, err := c.FormFile("file") if err != nil { - return err + log.Println(err) + return c.JSON(http.StatusBadRequest, map[string]interface{}{ + "message": "Error during handleV1DocumentCreate's echo#Context.FormFile", + "error": err, + }) } allowed := false @@ -729,13 +733,41 @@ func handleAdminDocumentsUpload(c echo.Context) error { } func handleAdminDocumentsUploadPOST(c echo.Context) error { - data := struct { - Message string - }{ - Message: "Pas implémenté", + client, err := api.New("http", "localhost", viper.GetInt("server.port"), api.APIOptions{ + KeyAuth: viper.GetBool("server.api.auth"), + Key: viper.GetString("server.api.key"), + BasicAuth: viper.GetBool("server.admin.auth"), + Username: viper.GetString("server.admin.username"), + Password: viper.GetString("server.admin.password"), + }) + if err != nil { + return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) } - return c.Render(http.StatusOK, "admin-upload-html", data) + bucket := c.FormValue("bucket") + + document, err := c.FormFile("document") + if err != nil { + //return c.JSON(http.StatusBadRequest, map[string]string{"message": "Error during file parse"}) + return c.JSON(http.StatusNotFound, map[string]string{"message": "Error during echo#Context.FormFile", "error": err.Error()}) + } + + /* + file, err := document.Open() + if err != nil { + //return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) + return c.JSON(http.StatusNotFound, map[string]string{"message": "Error during file.Open()", "error": err.Error()}) + } + defer file.Close() + */ + + response, err := client.UploadDocument(bucket, document) + if err != nil { + return c.JSON(http.StatusNotFound, map[string]string{"message": "Error duing api#client.UploadDocument", "error": err.Error()}) + //return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) + } + + return c.Render(http.StatusOK, "admin-upload-html", struct{ Message string }{Message: response}) } // CSS Handlers diff --git a/public/html/admin-upload.gohtml b/public/html/admin-upload.gohtml index 7b80e9b..b7ff836 100644 --- a/public/html/admin-upload.gohtml +++ b/public/html/admin-upload.gohtml @@ -9,6 +9,19 @@ {{ template "header-html" }}

Upload

+
+ + + +
+ Document: +
+
+ +

{{ .Message }}

-- 2.45.2 From cc4ab5851d4a81f3f71e477c809f38e3adfb15ff Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Fri, 28 Apr 2023 16:52:16 -0400 Subject: [PATCH 5/7] Fix upload de documents depuis DocumentCreate --- api/api.go | 71 +++++++++++++++++++++++++++------------------------ cmd/server.go | 2 +- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/api/api.go b/api/api.go index b89346f..6cbe09d 100644 --- a/api/api.go +++ b/api/api.go @@ -5,8 +5,8 @@ import ( "encoding/json" "errors" "fmt" + "io" "io/ioutil" - "log" "mime/multipart" "net/http" ) @@ -116,50 +116,55 @@ func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) ( a.Port, ) - route := fmt.Sprintf("/v1/bucket/%s", bucket) + url := fmt.Sprintf("%s/v1/bucket/%s", endpoint, bucket) - request := fmt.Sprintf("%s%s", endpoint, route) + // Create a new multipart writer + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) - client := &http.Client{} - - // set the HTTP method, url, and request body - req, err := http.NewRequest(http.MethodPost, request, nil) + // Add the file to the request + file, err := file_header.Open() if err != nil { - return "", err + return "", fmt.Errorf("UploadDocument#file_header.Open: %s", err) } + defer file.Close() + + part, err := writer.CreateFormFile("document", file_header.Filename) + if err != nil { + return "", fmt.Errorf("UploadDocument#writer.CreateFormFile: %s", err) + } + + _, err = io.Copy(part, file) + if err != nil { + return "", fmt.Errorf("UploadDocument#io.Copy: %s", err) + } + + err = writer.Close() + if err != nil { + return "", fmt.Errorf("UploadDocument#writer.Close: %s", err) + } + + // Create a new HTTP request with the multipart body + req, err := http.NewRequest(http.MethodPost, url, body) + if err != nil { + return "", fmt.Errorf("UploadDocument#http.NewRequest: %s", err) + } + + req.Header.Set("Content-Type", writer.FormDataContentType()) if a.Opts.KeyAuth { - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Opts.Key)) + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", a.Opts.Key)) } - req.Header.Add("Content-Type", "multipart/form-data") - - // CreateFormFile with multipart.NewWriter - //TODO - /* - buf := new(bytes.Buffer) - form := multipart.NewWriter(buf) - form.CreateFormFile("document", ) - */ - - //TODO - log.Println("req: ", req) - - file, file_header, err := req.FormFile("document") - if err != nil { - log.Println("Error during http#Request.Formfile") - return "", err - } - - log.Println("file: ", file, "file_header: ", file_header) - log.Println("file_header: ", file_header) - + // Send the HTTP request + client := &http.Client{} resp, err := client.Do(req) if err != nil { - log.Println("Error during http#Client.Do") - return "", err + return "", fmt.Errorf("UploadDocument#client.Do: %s", err) } + defer resp.Body.Close() + // Handle the response var res map[string]interface{} json.NewDecoder(resp.Body).Decode(&res) diff --git a/cmd/server.go b/cmd/server.go index a19fd5d..b0d55eb 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -386,7 +386,7 @@ func handleV1DocumentCreate(c echo.Context) error { bucket := c.Param("bucket") - form_file, err := c.FormFile("file") + form_file, err := c.FormFile("document") if err != nil { log.Println(err) return c.JSON(http.StatusBadRequest, map[string]interface{}{ -- 2.45.2 From 5d984ccacb977997f7df8b50e2362ddfa838fde9 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Fri, 28 Apr 2023 17:30:08 -0400 Subject: [PATCH 6/7] =?UTF-8?q?Retirer=20JSON=20de=20form=20cr=C3=A9ation?= =?UTF-8?q?=20de=20document?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Utiliser context#Render pour afficher une string Message à l'écran. --- api/api.go | 16 ++++++++-------- cmd/server.go | 36 +++++++++++++++++++++--------------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/api/api.go b/api/api.go index 6cbe09d..2de03f8 100644 --- a/api/api.go +++ b/api/api.go @@ -109,7 +109,7 @@ func (a *API) Call(method, route string) ([]byte, error) { 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) (string, error) { +func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) (map[string]interface{}, error) { endpoint := fmt.Sprintf("%s://%s:%d", a.Protocol, a.Host, @@ -125,29 +125,29 @@ func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) ( // Add the file to the request file, err := file_header.Open() if err != nil { - return "", fmt.Errorf("UploadDocument#file_header.Open: %s", err) + return nil, fmt.Errorf("UploadDocument#file_header.Open: %s", err) } defer file.Close() part, err := writer.CreateFormFile("document", file_header.Filename) if err != nil { - return "", fmt.Errorf("UploadDocument#writer.CreateFormFile: %s", err) + return nil, fmt.Errorf("UploadDocument#writer.CreateFormFile: %s", err) } _, err = io.Copy(part, file) if err != nil { - return "", fmt.Errorf("UploadDocument#io.Copy: %s", err) + return nil, fmt.Errorf("UploadDocument#io.Copy: %s", err) } err = writer.Close() if err != nil { - return "", fmt.Errorf("UploadDocument#writer.Close: %s", err) + return nil, fmt.Errorf("UploadDocument#writer.Close: %s", err) } // Create a new HTTP request with the multipart body req, err := http.NewRequest(http.MethodPost, url, body) if err != nil { - return "", fmt.Errorf("UploadDocument#http.NewRequest: %s", err) + return nil, fmt.Errorf("UploadDocument#http.NewRequest: %s", err) } req.Header.Set("Content-Type", writer.FormDataContentType()) @@ -160,7 +160,7 @@ func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) ( client := &http.Client{} resp, err := client.Do(req) if err != nil { - return "", fmt.Errorf("UploadDocument#client.Do: %s", err) + return nil, fmt.Errorf("UploadDocument#client.Do: %s", err) } defer resp.Body.Close() @@ -168,7 +168,7 @@ func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) ( var res map[string]interface{} json.NewDecoder(resp.Body).Decode(&res) - return fmt.Sprintf("%s", res), nil + return res, nil } // CallWithData takes data and returns a string representing a response body. diff --git a/cmd/server.go b/cmd/server.go index b0d55eb..d3f0ca8 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -741,33 +741,39 @@ func handleAdminDocumentsUploadPOST(c echo.Context) error { Password: viper.GetString("server.admin.password"), }) if err != nil { - return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) + return c.Render(http.StatusInternalServerError, "admin-upload-html", struct{ Message string }{Message: fmt.Sprintf("handleAdminDocumentsUploadPOST#api.New: %s", err)}) } bucket := c.FormValue("bucket") document, err := c.FormFile("document") if err != nil { - //return c.JSON(http.StatusBadRequest, map[string]string{"message": "Error during file parse"}) - return c.JSON(http.StatusNotFound, map[string]string{"message": "Error during echo#Context.FormFile", "error": err.Error()}) + return c.Render(http.StatusBadRequest, "admin-upload-html", struct{ Message string }{Message: fmt.Sprintf("handleAdminDocumentsUploadPOST#c.FormFile: %s", err)}) } - /* - file, err := document.Open() - if err != nil { - //return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) - return c.JSON(http.StatusNotFound, map[string]string{"message": "Error during file.Open()", "error": err.Error()}) - } - defer file.Close() - */ - response, err := client.UploadDocument(bucket, document) if err != nil { - return c.JSON(http.StatusNotFound, map[string]string{"message": "Error duing api#client.UploadDocument", "error": err.Error()}) - //return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) + return c.Render(http.StatusInternalServerError, "admin-upload-html", struct{ Message string }{Message: fmt.Sprintf("handleAdminDocumentsUploadPOST#client.UploadDocument: %s", err)}) } - return c.Render(http.StatusOK, "admin-upload-html", struct{ Message string }{Message: response}) + // Format response + var message, info, status string + + for key, value := range response { + switch key { + case "info": + info_map, ok := value.(map[string]interface{}) + if ok { + info = fmt.Sprintf("/public/documentation/%s/%s [%.2f]", info_map["bucket"], info_map["key"], info_map["size"]) + } + case "message": + status = fmt.Sprint(value) + } + } + + message = fmt.Sprintf("%s - %s", status, info) + + return c.Render(http.StatusOK, "admin-upload-html", struct{ Message string }{Message: message}) } // CSS Handlers -- 2.45.2 From 3dd4dd6e29927f5b158181f9e214995c359b3354 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Fri, 28 Apr 2023 19:30:31 -0400 Subject: [PATCH 7/7] =?UTF-8?q?Fix=20affichage=20de=20r=C3=A9ponse=20?= =?UTF-8?q?=C3=A0=20cr=C3=A9ation=20et=20/documentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Retirer caractères spéciaux lors de l'import --- api/api.go | 27 ++++++++++++++++++-------- cmd/server.go | 54 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 52 insertions(+), 29 deletions(-) diff --git a/api/api.go b/api/api.go index 2de03f8..c7d270d 100644 --- a/api/api.go +++ b/api/api.go @@ -26,6 +26,17 @@ type APIOptions struct { Password string } +type UploadDocumentResponse struct { + Info UploadDocumentResponseInfo `json:"info"` + Message string `json:"message"` +} + +type UploadDocumentResponseInfo struct { + Bucket string `json:"bucket"` + Object string `json:"key"` + Size float64 `json:"size"` +} + func New(protocol, host string, port int, opts APIOptions) (*API, error) { api := API{ Protocol: protocol, @@ -109,7 +120,7 @@ func (a *API) Call(method, route string) ([]byte, error) { 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) (map[string]interface{}, error) { +func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) (UploadDocumentResponse, error) { endpoint := fmt.Sprintf("%s://%s:%d", a.Protocol, a.Host, @@ -125,29 +136,29 @@ func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) ( // Add the file to the request file, err := file_header.Open() if err != nil { - return nil, fmt.Errorf("UploadDocument#file_header.Open: %s", err) + return UploadDocumentResponse{}, fmt.Errorf("UploadDocument#file_header.Open: %s", err) } defer file.Close() part, err := writer.CreateFormFile("document", file_header.Filename) if err != nil { - return nil, fmt.Errorf("UploadDocument#writer.CreateFormFile: %s", err) + return UploadDocumentResponse{}, fmt.Errorf("UploadDocument#writer.CreateFormFile: %s", err) } _, err = io.Copy(part, file) if err != nil { - return nil, fmt.Errorf("UploadDocument#io.Copy: %s", err) + return UploadDocumentResponse{}, fmt.Errorf("UploadDocument#io.Copy: %s", err) } err = writer.Close() if err != nil { - return nil, fmt.Errorf("UploadDocument#writer.Close: %s", err) + return UploadDocumentResponse{}, fmt.Errorf("UploadDocument#writer.Close: %s", err) } // Create a new HTTP request with the multipart body req, err := http.NewRequest(http.MethodPost, url, body) if err != nil { - return nil, fmt.Errorf("UploadDocument#http.NewRequest: %s", err) + return UploadDocumentResponse{}, fmt.Errorf("UploadDocument#http.NewRequest: %s", err) } req.Header.Set("Content-Type", writer.FormDataContentType()) @@ -160,12 +171,12 @@ func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) ( client := &http.Client{} resp, err := client.Do(req) if err != nil { - return nil, fmt.Errorf("UploadDocument#client.Do: %s", err) + return UploadDocumentResponse{}, fmt.Errorf("UploadDocument#client.Do: %s", err) } defer resp.Body.Close() // Handle the response - var res map[string]interface{} + var res UploadDocumentResponse json.NewDecoder(resp.Body).Decode(&res) return res, nil diff --git a/cmd/server.go b/cmd/server.go index d3f0ca8..761d343 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -9,6 +9,7 @@ import ( "encoding/json" "fmt" "log" + "regexp" "embed" "html/template" @@ -388,7 +389,6 @@ func handleV1DocumentCreate(c echo.Context) error { form_file, err := c.FormFile("document") if err != nil { - log.Println(err) return c.JSON(http.StatusBadRequest, map[string]interface{}{ "message": "Error during handleV1DocumentCreate's echo#Context.FormFile", "error": err, @@ -403,11 +403,6 @@ func handleV1DocumentCreate(c echo.Context) error { } if !allowed { - /* - return c.JSON(http.StatusBadRequest, map[string]string{ - "message": "Bucket is not allowed in server.documents.buckets", - }) - */ return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) } @@ -432,7 +427,14 @@ func handleV1DocumentCreate(c echo.Context) error { } defer src.Close() - info, err := client.PutObject(ctx, bucket, form_file.Filename, src, form_file.Size, minio.PutObjectOptions{ + reg, err := regexp.Compile("[^.a-zA-Z0-9_-]+") + if err != nil { + return c.Render(http.StatusInternalServerError, "documentation-html", nil) + } + + filename_processed := reg.ReplaceAllString(form_file.Filename, "") + + info, err := client.PutObject(ctx, bucket, filename_processed, src, form_file.Size, minio.PutObjectOptions{ ContentType: form_file.Header.Get("Content-Type"), }) if err != nil { @@ -671,21 +673,39 @@ func handleDocumentation(c echo.Context) error { var data []Bucket for _, bucket := range buckets { - result, err := client.Call(http.MethodGet, fmt.Sprintf("/v1/bucket/%s", bucket)) + content, err := client.Call(http.MethodGet, fmt.Sprintf("/v1/bucket/%s", bucket)) if err != nil { return c.Render(http.StatusInternalServerError, "documentation-html", nil) } var documents []string - err = json.Unmarshal(result, &documents) + err = json.Unmarshal(content, &documents) if err != nil { return c.Render(http.StatusInternalServerError, "documentation-html", nil) } + // Ce bloc retire tous les caractères spéciaux d'une string + // N'est pas présentement activé, car les fichiers sont processed + // à la création de toute façon. + /* + reg, err := regexp.Compile("[^.a-zA-Z0-9_-]+") + if err != nil { + return c.Render(http.StatusInternalServerError, "documentation-html", nil) + } + + var documents_processed []string + + for _, document := range documents { + document_processed := reg.ReplaceAllString(document, "") + documents_processed = append(documents_processed, document_processed) + } + */ + documents_processed := documents + data = append(data, Bucket{ Name: bucket, - Documents: documents, + Documents: documents_processed, }) } @@ -759,17 +779,9 @@ func handleAdminDocumentsUploadPOST(c echo.Context) error { // Format response var message, info, status string - for key, value := range response { - switch key { - case "info": - info_map, ok := value.(map[string]interface{}) - if ok { - info = fmt.Sprintf("/public/documentation/%s/%s [%.2f]", info_map["bucket"], info_map["key"], info_map["size"]) - } - case "message": - status = fmt.Sprint(value) - } - } + info = fmt.Sprintf("[%.0f] /public/documentation/%s/%s", response.Info.Size, response.Info.Bucket, response.Info.Object) + + status = response.Message message = fmt.Sprintf("%s - %s", status, info) -- 2.45.2