From 106e903b14c4cc6af10495e3ea7834bb3db7d144 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Thu, 22 Aug 2024 13:10:46 -0400 Subject: [PATCH] merge: handlers dans handler.go --- apihandler/apihandler.go | 27 -- apihandler/bucket.go | 110 ------ apihandler/document.go | 400 ------------------- apihandler/seed.go | 35 -- apihandler/spec.go | 36 -- handler.go | 806 +++++++++++++++++++++++++++++++++++++++ webhandler/webhandler.go | 254 ------------ 7 files changed, 806 insertions(+), 862 deletions(-) delete mode 100644 apihandler/apihandler.go delete mode 100644 apihandler/bucket.go delete mode 100644 apihandler/document.go delete mode 100644 apihandler/seed.go delete mode 100644 apihandler/spec.go create mode 100644 handler.go delete mode 100644 webhandler/webhandler.go diff --git a/apihandler/apihandler.go b/apihandler/apihandler.go deleted file mode 100644 index 4d0c96b..0000000 --- a/apihandler/apihandler.go +++ /dev/null @@ -1,27 +0,0 @@ -package apihandler - -import ( - "net/http" - "sort" - - "codeberg.org/vlbeaudoin/pave/v2" - "git.agecem.com/agecem/agecem-org/config" - "git.agecem.com/agecem/agecem-org/media" - "github.com/labstack/echo/v4" -) - -type V1Handler struct { - Config config.Config - MediaClient *media.MediaClient - Pave *pave.Pave -} - -// API Handlers - -// V1GET affiche les routes accessibles. -// Les routes sont triées selon .Path, pour les rendre plus facilement navigables. -func (h *V1Handler) V1GET(c echo.Context) error { - routes := c.Echo().Routes() - sort.Slice(routes, func(i, j int) bool { return routes[i].Path < routes[j].Path }) - return c.JSON(http.StatusOK, routes) -} diff --git a/apihandler/bucket.go b/apihandler/bucket.go deleted file mode 100644 index 3cb37ee..0000000 --- a/apihandler/bucket.go +++ /dev/null @@ -1,110 +0,0 @@ -package apihandler - -import ( - "context" - "net/http" - - "codeberg.org/vlbeaudoin/voki/v3" - "git.agecem.com/agecem/agecem-org/apirequest" - "git.agecem.com/agecem/agecem-org/apiresponse" - "github.com/labstack/echo/v4" - "github.com/minio/minio-go/v7" -) - -// V1BucketsGET affiche les buckets permis par server.documents.buckets, qui existent. -func (h *V1Handler) V1BucketsGET(c echo.Context) error { - var request apirequest.V1BucketsGET - var response apiresponse.V1BucketsGET - - if !request.Complete() { - response.Message = "Incomplete V1BucketsGET request received" - response.SetStatusCode(http.StatusBadRequest) - - return c.JSON(response.StatusCode(), response) - } - - var buckets = make(map[string]string) - - for bucket_name, bucket_display_name := range h.Config.Server.Documents.Buckets { - exists, err := h.MediaClient.MinioClient.BucketExists(context.Background(), bucket_name) - if err != nil { - response.Message = "Error during minio#BucketExists" - response.SetStatusCode(http.StatusInternalServerError) - - return c.JSON(response.StatusCode(), response) - } - - if exists { - buckets[bucket_name] = bucket_display_name - } - } - - response.SetStatusCode(http.StatusOK) - response.Message = "Buckets list successful" - response.Data.Buckets = buckets - - return c.JSON(response.StatusCode(), response) -} - -func (h *V1Handler) V1BucketGET(c echo.Context) error { - var request apirequest.V1BucketGET - var response apiresponse.V1BucketGET - - request.Params.Bucket = c.Param("bucket") - - if !request.Complete() { - response.Message = "Incomplete V1BucketGET request received" - response.SetStatusCode(http.StatusBadRequest) - - return c.JSON(response.StatusCode(), response) - } - - allowed := false - for bucket_allowed := range h.Config.Server.Documents.Buckets { - if request.Params.Bucket == bucket_allowed { - allowed = true - } - } - - if !allowed { - response := voki.ResponseNotFound{} - return c.JSON(response.StatusCode(), response) - } - - ctx, cancel := context.WithCancel(context.Background()) - - defer cancel() - - exists, err := h.MediaClient.MinioClient.BucketExists(ctx, request.Params.Bucket) - if err != nil { - response.Message = "Error during minio#BucketExists" - response.SetStatusCode(http.StatusInternalServerError) - response.Error = err.Error() - - return c.JSON(response.StatusCode(), response) - } - - if !exists { - response := voki.ResponseNotFound{} - return c.JSON(response.StatusCode(), response) - } - - objectCh := h.MediaClient.MinioClient.ListObjects(ctx, request.Params.Bucket, minio.ListObjectsOptions{}) - for object := range objectCh { - if object.Err != nil { - response.Message = "Error during minio#ListObjects" - response.SetStatusCode(http.StatusInternalServerError) - //TODO make sure this is safe - //response.Error = object.Err.Error() - - return c.JSON(response.StatusCode(), response) - } - - response.Data.Keys = append(response.Data.Keys, object.Key) - } - - response.Message = "V1BucketRead ok" - response.SetStatusCode(http.StatusOK) - - return c.JSON(response.StatusCode(), response) -} diff --git a/apihandler/document.go b/apihandler/document.go deleted file mode 100644 index 274ba25..0000000 --- a/apihandler/document.go +++ /dev/null @@ -1,400 +0,0 @@ -package apihandler - -import ( - "context" - "fmt" - "net/http" - - "codeberg.org/vlbeaudoin/voki/v3" - "git.agecem.com/agecem/agecem-org/apirequest" - "git.agecem.com/agecem/agecem-org/apiresponse" - "github.com/labstack/echo/v4" - "github.com/minio/minio-go/v7" -) - -/* -V1DocumentsPOST permet d'ajouter un object dans un bucket, par multipart/form-data - -Example: - -Téléverser plusieurs fichiers à cette route avec `curl`: - - curl -F 'documents=@example.pdf' -F 'documents=@example.md;type=text/markdown' -*/ -func (h *V1Handler) V1DocumentsPOST(c echo.Context) (err error) { - var request apirequest.CreateDocumentsResponse - var response apiresponse.V1DocumentsPOST - - request.Params.Bucket = c.Param("bucket") - - var allowed bool - for allowedBucket := range h.Config.Server.Documents.Buckets { - if request.Params.Bucket == allowedBucket { - allowed = true - } - } - - if !allowed { - response := voki.ResponseNotFound{} - return c.JSON(response.StatusCode(), response) - } - - form, err := c.MultipartForm() - if err != nil { - response.Message = fmt.Sprintf("Téléversement invalide: %s", err) - response.SetStatusCode(http.StatusBadRequest) - - return c.JSON(response.StatusCode(), response) - } - if form == nil { - response.Message = "MultipartForm pointe vers une addresse mémoire nil" - response.SetStatusCode(http.StatusBadRequest) - - return c.JSON(response.StatusCode(), response) - } - - if len(form.File) == 0 { - response.Message = "Veuillez sélectionner au moins 1 document à téléverser" - response.SetStatusCode(http.StatusBadRequest) - - return c.JSON(response.StatusCode(), response) - } - - for inputName, inputFileHeaders := range form.File { - if inputName == "documents" { - request.Data.Documents = inputFileHeaders - } - } - - if request.Data.Documents == nil { - response.Message = "Impossible d'obtenir les documents depuis le formulaire" - response.SetStatusCode(http.StatusBadRequest) - - return c.JSON(response.StatusCode(), response) - } - - if !request.Complete() { - response.Message = "Requête V1DocumentsPOST incomplète reçue" - response.SetStatusCode(http.StatusBadRequest) - - return c.JSON(response.StatusCode(), response) - } - - var code int - code, response.Message = h.MediaClient.UploadFormFiles(request.Params.Bucket, request.Data.Documents) - response.SetStatusCode(code) - - return c.JSON(response.StatusCode(), response) -} - -// V1DocumentPOST permet d'ajouter un object dans un bucket, par multipart/form-data -func (h *V1Handler) V1DocumentPOST(c echo.Context) (err error) { - var request apirequest.V1DocumentPOST - var response apiresponse.V1DocumentPOST - - request.Params.Bucket = c.Param("bucket") - - request.Data.Document, err = c.FormFile("document") - if err != nil { - response.SetStatusCode(http.StatusBadRequest) - response.Message = "Error during HandleV1DocumentCreate's echo#Context.FormFile" - response.Error = err.Error() - - return c.JSON(response.StatusCode(), response) - } - - allowed := false - for bucket_allowed := range h.Config.Server.Documents.Buckets { - if request.Params.Bucket == bucket_allowed { - allowed = true - } - } - - if !allowed { - response := voki.ResponseNotFound{} - return c.JSON(response.StatusCode(), response) - } - - if !request.Complete() { - response.Message = "Incomplete V1DocumentPOST request received" - response.SetStatusCode(http.StatusBadRequest) - - return c.JSON(response.StatusCode(), response) - } - - ctx, cancel := context.WithCancel(context.Background()) - - defer cancel() - - src, err := request.Data.Document.Open() - if err != nil { - response.SetStatusCode(http.StatusBadRequest) - response.Message = "Error during form_file.Open()" - response.Error = err.Error() - - return c.JSON(response.StatusCode(), response) - } - defer src.Close() - - info, err := h.MediaClient.MinioClient.PutObject(ctx, request.Params.Bucket, request.Data.Document.Filename, src, request.Data.Document.Size, minio.PutObjectOptions{ - ContentType: request.Data.Document.Header.Get("Content-Type"), - }) - if err != nil { - response.SetStatusCode(http.StatusInternalServerError) - response.Message = "Error during minio#PutObject" - //response.Error = err.Error() - - return c.JSON(response.StatusCode(), response) - } - - response.SetStatusCode(http.StatusOK) - response.Message = "ok" - response.Data.Bucket = info.Bucket - response.Data.Key = info.Key - response.Data.Size = info.Size - - return c.JSON(response.StatusCode(), response) -} - -// V1DocumentGET permet de lire le contenu d'un fichier et protentiellement de le télécharger -func (h *V1Handler) V1DocumentGET(c echo.Context) error { - bucket := c.Param("bucket") - document := c.Param("document") - - allowed := false - for bucket_allowed := range h.Config.Server.Documents.Buckets { - if bucket == bucket_allowed { - allowed = true - } - } - - if !allowed { - response := voki.ResponseNotFound{} - return c.JSON(response.StatusCode(), response) - } - - ctx, cancel := context.WithCancel(context.Background()) - - defer cancel() - - bucket_exists, err := h.MediaClient.MinioClient.BucketExists(ctx, bucket) - if err != nil { - return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") - } - - if !bucket_exists { - response := voki.ResponseNotFound{} - return c.JSON(response.StatusCode(), response) - } - - document_info, err := h.MediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{}) - - if err != nil { - if err.Error() == "The specified key does not exist." { - - response := voki.ResponseNotFound{} - return c.JSON(response.StatusCode(), response) - } - - return c.JSON(http.StatusInternalServerError, map[string]interface{}{ - "message": "Error during minio#StatObject", - }) - } - - _ = document_info - - document_object, err := h.MediaClient.MinioClient.GetObject(ctx, bucket, document, minio.GetObjectOptions{}) - if err != nil { - return c.JSON(http.StatusInternalServerError, map[string]string{ - "message": "Error during minio#GetObject", - }) - } - - defer document_object.Close() - - return c.Stream(http.StatusOK, document_info.ContentType, document_object) -} - -// V1DocumentDELETE permet de supprimer un object -func (h *V1Handler) V1DocumentDELETE(c echo.Context) error { - var request apirequest.V1DocumentDELETE - var response apiresponse.V1DocumentDELETE - - request.Params.Bucket = c.Param("bucket") - request.Params.Document = c.Param("document") - - allowed := false - for bucket_allowed := range h.Config.Server.Documents.Buckets { - if request.Params.Bucket == bucket_allowed { - allowed = true - } - } - - if !allowed { - response := voki.ResponseNotFound{} - return c.JSON(response.StatusCode(), response) - } - - if !request.Complete() { - response.Message = "Incomplete V1DocumentDELETE request received" - response.SetStatusCode(http.StatusBadRequest) - - return c.JSON(response.StatusCode(), response) - } - - ctx, cancel := context.WithCancel(context.Background()) - - defer cancel() - - bucket_exists, err := h.MediaClient.MinioClient.BucketExists(ctx, request.Params.Bucket) - if err != nil { - return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") - } - - if !bucket_exists { - response := voki.ResponseNotFound{} - return c.JSON(response.StatusCode(), response) - } - - document_info, err := h.MediaClient.MinioClient.StatObject(ctx, request.Params.Bucket, request.Params.Document, minio.StatObjectOptions{}) - if err != nil { - if err.Error() == "The specified key does not exist." { - - response := voki.ResponseNotFound{} - return c.JSON(response.StatusCode(), response) - } - - //response.Error = err.Error() - response.Message = "Error during minio#StatObject" - response.SetStatusCode(http.StatusInternalServerError) - - return c.JSON(response.StatusCode(), response) - } - - //TODO Add error validation - _ = document_info - - err = h.MediaClient.MinioClient.RemoveObject(ctx, request.Params.Bucket, request.Params.Document, minio.RemoveObjectOptions{}) - if err != nil { - //response.Error = err.Error() - response.Message = "Error during minio#RemoveObject" - response.SetStatusCode(http.StatusInternalServerError) - - return c.JSON(response.StatusCode(), response) - } - - response.Message = "Document deleted" - response.SetStatusCode(http.StatusOK) - - return c.JSON(response.StatusCode(), response) -} - -// V1DocumentKeyPUT -func (h *V1Handler) V1DocumentKeyPUT(c echo.Context) (err error) { - var request apirequest.V1DocumentKeyPUT - var response apiresponse.V1DocumentKeyPUT - - bucket := c.Param("bucket") - document := c.Param("document") - - var newKey string - err = c.Bind(&newKey) - if err != nil { - response.SetStatusCode(http.StatusBadRequest) - response.Message = err.Error() - - return c.JSON(response.StatusCode(), response) - } - - request, err = apirequest.NewV1DocumentKeyPUT(bucket, document, newKey) - if err != nil { - response.SetStatusCode(http.StatusBadRequest) - response.Message = err.Error() - - return c.JSON(response.StatusCode(), response) - } - - if !request.Complete() { - response.SetStatusCode(http.StatusBadRequest) - response.Message = "Incomplete V1DocumentKeyPUT request received" - - return c.JSON(response.StatusCode(), response) - } - - var allowed bool - for bucketAllowed := range h.Config.Server.Documents.Buckets { - if bucket == bucketAllowed { - allowed = true - } - } - - if !allowed { - response := voki.ResponseNotFound{} - return c.JSON(response.StatusCode(), response) - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - bucketExists, err := h.MediaClient.MinioClient.BucketExists(ctx, request.Params.Bucket) - if err != nil { - return c.JSON(http.StatusInternalServerError, "Could not validate bucket exists") - } - - if !bucketExists { - response := voki.ResponseNotFound{} - return c.JSON(response.StatusCode(), response) - } - - // Check source object exists - - if _, err := h.MediaClient.MinioClient.StatObject(ctx, request.Params.Bucket, request.Params.Document, minio.StatObjectOptions{}); err != nil { - if err.Error() == "The specified key does not exist." { - - response := voki.ResponseNotFound{} - return c.JSON(response.StatusCode(), response) - } - - response.Message = fmt.Sprintf("Could not obtain information on %s/%s", request.Params.Bucket, request.Params.Document) - response.SetStatusCode(http.StatusInternalServerError) - - return c.JSON(response.StatusCode(), response) - } - - // Copy object to newKey - - if _, err := h.MediaClient.MinioClient.CopyObject(ctx, - minio.CopyDestOptions{Bucket: request.Params.Bucket, Object: request.Data.NewKey}, - minio.CopySrcOptions{Bucket: request.Params.Bucket, Object: request.Params.Document}, - ); err != nil { - response.SetStatusCode(http.StatusInternalServerError) - response.Message = "Impossible de copier un document pour le renommer" - return c.JSON(response.StatusCode(), response) - } - - // Verify copy was successful - if _, err := h.MediaClient.MinioClient.StatObject(ctx, - request.Params.Bucket, request.Data.NewKey, minio.GetObjectOptions{}); err != nil { - response.SetStatusCode(http.StatusInternalServerError) - response.Message = "Copie de document ne semble pas avoir fonctionnée" - return c.JSON(response.StatusCode(), response) - } - - // Delete old file - if err := h.MediaClient.MinioClient.RemoveObject(ctx, - request.Params.Bucket, request.Params.Document, minio.RemoveObjectOptions{}); err != nil { - response.SetStatusCode(http.StatusInternalServerError) - response.Message = "Erreur pendant tentative de supprimer document source" - return c.JSON(response.StatusCode(), response) - } - - //TODO cleanup - - // Return result - response.SetStatusCode(http.StatusOK) - response.Message = "Document renommé avec succès" - response.Data.Bucket = request.Params.Bucket - response.Data.Key = request.Data.NewKey - return c.JSON(response.StatusCode(), response) - -} diff --git a/apihandler/seed.go b/apihandler/seed.go deleted file mode 100644 index 9462eca..0000000 --- a/apihandler/seed.go +++ /dev/null @@ -1,35 +0,0 @@ -package apihandler - -import ( - "net/http" - - "git.agecem.com/agecem/agecem-org/apiresponse" - "github.com/labstack/echo/v4" -) - -// V1SeedPOST 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) V1SeedPOST(c echo.Context) error { - var response apiresponse.V1SeedPOST - - new_buckets, err := h.MediaClient.Seed() - response.Data.Buckets = new_buckets - if err != nil { - response.SetStatusCode(http.StatusInternalServerError) - response.Message = "Error during mediaClient.Seed()" - response.Error = err.Error() - - return c.JSON(response.StatusCode(), response) - } - - if len(new_buckets) == 0 { - response.Message = "All buckets already exist" - - } else { - response.Message = "Buckets successfully created" - } - - response.SetStatusCode(http.StatusOK) - - return c.JSON(response.StatusCode(), response) -} diff --git a/apihandler/spec.go b/apihandler/spec.go deleted file mode 100644 index afc69c4..0000000 --- a/apihandler/spec.go +++ /dev/null @@ -1,36 +0,0 @@ -package apihandler - -import ( - "fmt" - "net/http" - - "git.agecem.com/agecem/agecem-org/apirequest" - "git.agecem.com/agecem/agecem-org/apiresponse" - "git.agecem.com/agecem/agecem-org/version" - "github.com/labstack/echo/v4" -) - -const DescriptionV1SpecGET string = "Afficher le API spec en format pave" - -func (h *V1Handler) V1SpecGET(c echo.Context) error { - var request apirequest.V1SpecGET - var response apiresponse.V1SpecGET - - if !request.Complete() { - response.Message = "Incomplete V1SpecGET request received" - response.SetStatusCode(http.StatusBadRequest) - - return c.JSON(response.StatusCode(), response) - } - - response.Data.Spec = fmt.Sprintf("# pave spec for agecem-org %s\n", version.Version()) - - for _, route := range h.Pave.SortedRouteStrings() { - response.Data.Spec = fmt.Sprintf("%s%s", response.Data.Spec, route) - } - - response.Message = "ok" - response.SetStatusCode(http.StatusOK) - - return c.JSON(response.StatusCode(), response) -} diff --git a/handler.go b/handler.go new file mode 100644 index 0000000..abca06b --- /dev/null +++ b/handler.go @@ -0,0 +1,806 @@ +package main + +import ( + "context" + "fmt" + "io" + "net/http" + "net/url" + "sort" + + "codeberg.org/vlbeaudoin/pave/v2" + "codeberg.org/vlbeaudoin/voki/v3" + "git.agecem.com/agecem/agecem-org/api" + "git.agecem.com/agecem/agecem-org/config" + "git.agecem.com/agecem/agecem-org/media" + "git.agecem.com/agecem/agecem-org/models" + "git.agecem.com/agecem/agecem-org/version" + "github.com/labstack/echo/v4" + "github.com/minio/minio-go/v7" +) + +type V1Handler struct { + Config config.Config + MediaClient *media.MediaClient + Pave *pave.Pave +} + +// API Handlers + +// ListRoutes affiche les routes accessibles. +// Les routes sont triées selon .Path, pour les rendre plus facilement navigables. +func (h *V1Handler) ListRoutes(c echo.Context) error { + routes := c.Echo().Routes() + sort.Slice(routes, func(i, j int) bool { return routes[i].Path < routes[j].Path }) + return c.JSON(http.StatusOK, routes) +} + +// ListBuckets affiche les buckets permis par server.documents.buckets, qui existent. +func (h *V1Handler) ListBuckets(c echo.Context) error { + var request ListBucketsRequest + var response ListBucketsResponse + + if !request.Complete() { + response.Message = "Incomplete ListBuckets request received" + response.SetStatusCode(http.StatusBadRequest) + + return c.JSON(response.StatusCode(), response) + } + + var buckets = make(map[string]string) + + for bucket_name, bucket_display_name := range h.Config.Server.Documents.Buckets { + exists, err := h.MediaClient.MinioClient.BucketExists(context.Background(), bucket_name) + if err != nil { + response.Message = "Error during minio#BucketExists" + response.SetStatusCode(http.StatusInternalServerError) + + return c.JSON(response.StatusCode(), response) + } + + if exists { + buckets[bucket_name] = bucket_display_name + } + } + + response.SetStatusCode(http.StatusOK) + response.Message = "Buckets list successful" + response.Data.Buckets = buckets + + return c.JSON(response.StatusCode(), response) +} + +func (h *V1Handler) ReadBucket(c echo.Context) error { + var request ReadBucketRequest + var response ReadBucketResponse + + request.Params.Bucket = c.Param("bucket") + + if !request.Complete() { + response.Message = "Incomplete ReadBucket request received" + response.SetStatusCode(http.StatusBadRequest) + + return c.JSON(response.StatusCode(), response) + } + + allowed := false + for bucket_allowed := range h.Config.Server.Documents.Buckets { + if request.Params.Bucket == bucket_allowed { + allowed = true + } + } + + if !allowed { + response := voki.ResponseNotFound{} + return c.JSON(response.StatusCode(), response) + } + + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + + exists, err := h.MediaClient.MinioClient.BucketExists(ctx, request.Params.Bucket) + if err != nil { + response.Message = "Error during minio#BucketExists" + response.SetStatusCode(http.StatusInternalServerError) + response.Error = err.Error() + + return c.JSON(response.StatusCode(), response) + } + + if !exists { + response := voki.ResponseNotFound{} + return c.JSON(response.StatusCode(), response) + } + + objectCh := h.MediaClient.MinioClient.ListObjects(ctx, request.Params.Bucket, minio.ListObjectsOptions{}) + for object := range objectCh { + if object.Err != nil { + response.Message = "Error during minio#ListObjects" + response.SetStatusCode(http.StatusInternalServerError) + //TODO make sure this is safe + //response.Error = object.Err.Error() + + return c.JSON(response.StatusCode(), response) + } + + response.Data.Keys = append(response.Data.Keys, object.Key) + } + + response.Message = "V1BucketRead ok" + response.SetStatusCode(http.StatusOK) + + return c.JSON(response.StatusCode(), response) +} + +/* +CreateDocuments permet d'ajouter un object dans un bucket, par multipart/form-data + +Example: + +Téléverser plusieurs fichiers à cette route avec `curl`: + + curl -F 'documents=@example.pdf' -F 'documents=@example.md;type=text/markdown' +*/ +func (h *V1Handler) CreateDocuments(c echo.Context) (err error) { + var request CreateDocumentsRequest + var response CreateDocumentsResponse + + request.Params.Bucket = c.Param("bucket") + + var allowed bool + for allowedBucket := range h.Config.Server.Documents.Buckets { + if request.Params.Bucket == allowedBucket { + allowed = true + } + } + + if !allowed { + response := voki.ResponseNotFound{} + return c.JSON(response.StatusCode(), response) + } + + form, err := c.MultipartForm() + if err != nil { + response.Message = fmt.Sprintf("Téléversement invalide: %s", err) + response.SetStatusCode(http.StatusBadRequest) + + return c.JSON(response.StatusCode(), response) + } + if form == nil { + response.Message = "MultipartForm pointe vers une addresse mémoire nil" + response.SetStatusCode(http.StatusBadRequest) + + return c.JSON(response.StatusCode(), response) + } + + if len(form.File) == 0 { + response.Message = "Veuillez sélectionner au moins 1 document à téléverser" + response.SetStatusCode(http.StatusBadRequest) + + return c.JSON(response.StatusCode(), response) + } + + for inputName, inputFileHeaders := range form.File { + if inputName == "documents" { + request.Data.Documents = inputFileHeaders + } + } + + if request.Data.Documents == nil { + response.Message = "Impossible d'obtenir les documents depuis le formulaire" + response.SetStatusCode(http.StatusBadRequest) + + return c.JSON(response.StatusCode(), response) + } + + if !request.Complete() { + response.Message = "Requête CreateDocuments incomplète reçue" + response.SetStatusCode(http.StatusBadRequest) + + return c.JSON(response.StatusCode(), response) + } + + var code int + code, response.Message = h.MediaClient.UploadFormFiles(request.Params.Bucket, request.Data.Documents) + response.SetStatusCode(code) + + return c.JSON(response.StatusCode(), response) +} + +// CreateDocument permet d'ajouter un object dans un bucket, par multipart/form-data +func (h *V1Handler) CreateDocument(c echo.Context) (err error) { + var request CreateDocumentRequest + var response CreateDocumentResponse + + request.Params.Bucket = c.Param("bucket") + + request.Data.Document, err = c.FormFile("document") + if err != nil { + response.SetStatusCode(http.StatusBadRequest) + response.Message = "Error during CreateDocument's echo#Context.FormFile" + response.Error = err.Error() + + return c.JSON(response.StatusCode(), response) + } + + allowed := false + for bucket_allowed := range h.Config.Server.Documents.Buckets { + if request.Params.Bucket == bucket_allowed { + allowed = true + } + } + + if !allowed { + response := voki.ResponseNotFound{} + return c.JSON(response.StatusCode(), response) + } + + if !request.Complete() { + response.Message = "Incomplete CreateDocument request received" + response.SetStatusCode(http.StatusBadRequest) + + return c.JSON(response.StatusCode(), response) + } + + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + + src, err := request.Data.Document.Open() + if err != nil { + response.SetStatusCode(http.StatusBadRequest) + response.Message = "Error during form_file.Open()" + response.Error = err.Error() + + return c.JSON(response.StatusCode(), response) + } + defer src.Close() + + info, err := h.MediaClient.MinioClient.PutObject(ctx, request.Params.Bucket, request.Data.Document.Filename, src, request.Data.Document.Size, minio.PutObjectOptions{ + ContentType: request.Data.Document.Header.Get("Content-Type"), + }) + if err != nil { + response.SetStatusCode(http.StatusInternalServerError) + response.Message = "Error during minio#PutObject" + //response.Error = err.Error() + + return c.JSON(response.StatusCode(), response) + } + + response.SetStatusCode(http.StatusOK) + response.Message = "ok" + response.Data.Bucket = info.Bucket + response.Data.Key = info.Key + response.Data.Size = info.Size + + return c.JSON(response.StatusCode(), response) +} + +// ReadDocument permet de lire le contenu d'un fichier et protentiellement de le télécharger +func (h *V1Handler) ReadDocument(c echo.Context) error { + bucket := c.Param("bucket") + document := c.Param("document") + + allowed := false + for bucket_allowed := range h.Config.Server.Documents.Buckets { + if bucket == bucket_allowed { + allowed = true + } + } + + if !allowed { + response := voki.ResponseNotFound{} + return c.JSON(response.StatusCode(), response) + } + + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + + bucket_exists, err := h.MediaClient.MinioClient.BucketExists(ctx, bucket) + if err != nil { + return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") + } + + if !bucket_exists { + response := voki.ResponseNotFound{} + return c.JSON(response.StatusCode(), response) + } + + document_info, err := h.MediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{}) + + if err != nil { + if err.Error() == "The specified key does not exist." { + + response := voki.ResponseNotFound{} + return c.JSON(response.StatusCode(), response) + } + + return c.JSON(http.StatusInternalServerError, map[string]interface{}{ + "message": "Error during minio#StatObject", + }) + } + + _ = document_info + + document_object, err := h.MediaClient.MinioClient.GetObject(ctx, bucket, document, minio.GetObjectOptions{}) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during minio#GetObject", + }) + } + + defer document_object.Close() + + return c.Stream(http.StatusOK, document_info.ContentType, document_object) +} + +// DeleteDocument permet de supprimer un object +func (h *V1Handler) DeleteDocument(c echo.Context) error { + var request DeleteDocumentRequest + var response DeleteDocumentResponse + + request.Params.Bucket = c.Param("bucket") + request.Params.Document = c.Param("document") + + allowed := false + for bucket_allowed := range h.Config.Server.Documents.Buckets { + if request.Params.Bucket == bucket_allowed { + allowed = true + } + } + + if !allowed { + response := voki.ResponseNotFound{} + return c.JSON(response.StatusCode(), response) + } + + if !request.Complete() { + response.Message = "Incomplete DeleteDocument request received" + response.SetStatusCode(http.StatusBadRequest) + + return c.JSON(response.StatusCode(), response) + } + + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + + bucket_exists, err := h.MediaClient.MinioClient.BucketExists(ctx, request.Params.Bucket) + if err != nil { + return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") + } + + if !bucket_exists { + response := voki.ResponseNotFound{} + return c.JSON(response.StatusCode(), response) + } + + document_info, err := h.MediaClient.MinioClient.StatObject(ctx, request.Params.Bucket, request.Params.Document, minio.StatObjectOptions{}) + if err != nil { + if err.Error() == "The specified key does not exist." { + + response := voki.ResponseNotFound{} + return c.JSON(response.StatusCode(), response) + } + + //response.Error = err.Error() + response.Message = "Error during minio#StatObject" + response.SetStatusCode(http.StatusInternalServerError) + + return c.JSON(response.StatusCode(), response) + } + + //TODO Add error validation + _ = document_info + + err = h.MediaClient.MinioClient.RemoveObject(ctx, request.Params.Bucket, request.Params.Document, minio.RemoveObjectOptions{}) + if err != nil { + //response.Error = err.Error() + response.Message = "Error during minio#RemoveObject" + response.SetStatusCode(http.StatusInternalServerError) + + return c.JSON(response.StatusCode(), response) + } + + response.Message = "Document deleted" + response.SetStatusCode(http.StatusOK) + + return c.JSON(response.StatusCode(), response) +} + +// UpdateDocumentKey +func (h *V1Handler) UpdateDocumentKey(c echo.Context) (err error) { + var request UpdateDocumentKeyRequest + var response UpdateDocumentKeyResponse + + request.Params.Bucket = c.Param("bucket") + request.Params.Document = c.Param("document") + + var newKey string + err = c.Bind(&newKey) + if err != nil { + response.SetStatusCode(http.StatusBadRequest) + response.Message = err.Error() + + return c.JSON(response.StatusCode(), response) + } + request.Data.NewKey = newKey + + if !request.Complete() { + response.SetStatusCode(http.StatusBadRequest) + response.Message = "Incomplete UpdateDocumentKey request received" + + return c.JSON(response.StatusCode(), response) + } + + var allowed bool + for bucketAllowed := range h.Config.Server.Documents.Buckets { + if request.Params.Bucket == bucketAllowed { + allowed = true + } + } + + if !allowed { + response := voki.ResponseNotFound{} + return c.JSON(response.StatusCode(), response) + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + bucketExists, err := h.MediaClient.MinioClient.BucketExists(ctx, request.Params.Bucket) + if err != nil { + return c.JSON(http.StatusInternalServerError, "Could not validate bucket exists") + } + + if !bucketExists { + response := voki.ResponseNotFound{} + return c.JSON(response.StatusCode(), response) + } + + // Check source object exists + + if _, err := h.MediaClient.MinioClient.StatObject(ctx, request.Params.Bucket, request.Params.Document, minio.StatObjectOptions{}); err != nil { + if err.Error() == "The specified key does not exist." { + + response := voki.ResponseNotFound{} + return c.JSON(response.StatusCode(), response) + } + + response.Message = fmt.Sprintf("Could not obtain information on %s/%s", request.Params.Bucket, request.Params.Document) + response.SetStatusCode(http.StatusInternalServerError) + + return c.JSON(response.StatusCode(), response) + } + + // Copy object to newKey + + if _, err := h.MediaClient.MinioClient.CopyObject(ctx, + minio.CopyDestOptions{Bucket: request.Params.Bucket, Object: request.Data.NewKey}, + minio.CopySrcOptions{Bucket: request.Params.Bucket, Object: request.Params.Document}, + ); err != nil { + response.SetStatusCode(http.StatusInternalServerError) + response.Message = "Impossible de copier un document pour le renommer" + return c.JSON(response.StatusCode(), response) + } + + // Verify copy was successful + if _, err := h.MediaClient.MinioClient.StatObject(ctx, + request.Params.Bucket, request.Data.NewKey, minio.GetObjectOptions{}); err != nil { + response.SetStatusCode(http.StatusInternalServerError) + response.Message = "Copie de document ne semble pas avoir fonctionnée" + return c.JSON(response.StatusCode(), response) + } + + // Delete old file + if err := h.MediaClient.MinioClient.RemoveObject(ctx, + request.Params.Bucket, request.Params.Document, minio.RemoveObjectOptions{}); err != nil { + response.SetStatusCode(http.StatusInternalServerError) + response.Message = "Erreur pendant tentative de supprimer document source" + return c.JSON(response.StatusCode(), response) + } + + //TODO cleanup + + // Return result + response.SetStatusCode(http.StatusOK) + response.Message = "Document renommé avec succès" + response.Data.Bucket = request.Params.Bucket + response.Data.Key = request.Data.NewKey + return c.JSON(response.StatusCode(), response) + +} + +// ExecuteSeed 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) ExecuteSeed(c echo.Context) error { + //var request ExecuteSeedRequest + var response ExecuteSeedResponse + + new_buckets, err := h.MediaClient.Seed() + response.Data.Buckets = new_buckets + if err != nil { + response.SetStatusCode(http.StatusInternalServerError) + response.Message = "Error during mediaClient.Seed()" + response.Error = err.Error() + + return c.JSON(response.StatusCode(), response) + } + + if len(new_buckets) == 0 { + response.Message = "All buckets already exist" + + } else { + response.Message = "Buckets successfully created" + } + + response.SetStatusCode(http.StatusOK) + + return c.JSON(response.StatusCode(), response) +} + +const DescriptionV1SpecGET string = "Afficher le API spec en format pave" + +func (h *V1Handler) ReadSpec(c echo.Context) error { + var request ReadSpecRequest + var response ReadSpecResponse + + if !request.Complete() { + response.Message = "Incomplete ReadSpec request received" + response.SetStatusCode(http.StatusBadRequest) + + return c.JSON(response.StatusCode(), response) + } + + response.Data.Spec = fmt.Sprintf("# pave spec for agecem-org %s\n", version.Version()) + + for _, route := range h.Pave.SortedRouteStrings() { + response.Data.Spec = fmt.Sprintf("%s%s", response.Data.Spec, route) + } + + response.Message = "ok" + response.SetStatusCode(http.StatusOK) + + return c.JSON(response.StatusCode(), response) +} + +type WebHandler struct { + ApiClient *api.API +} + +func HandleIndex(c echo.Context) error { + return c.Render(http.StatusOK, "index-html", nil) +} + +/* +func HandleAPropos(c echo.Context) error { + return c.Render(http.StatusOK, "a-propos-html", nil) +} +*/ + +/* +func HandleActualite(c echo.Context) error { + return c.Render(http.StatusOK, "actualite-html", nil) +} +*/ + +/* +func HandleActualiteArticle(c echo.Context) error { + article := c.Param("article") + return c.String(http.StatusOK, fmt.Sprintf("Article: %s", article)) +} +*/ + +func HandleVieEtudiante(c echo.Context) error { + return c.Render(http.StatusOK, "vie-etudiante-html", nil) +} + +func HandleVieEtudianteOrganisme(c echo.Context) error { + organisme := c.Param("organisme") + return c.String(http.StatusOK, fmt.Sprintf("Organisme: %s", organisme)) +} + +func (h *WebHandler) HandleDocumentation(c echo.Context) error { + var response HandleDocumentationResponse + + v1BucketsGET, err := h.ApiClient.ListBuckets() + if err != nil { + response.Error = err.Error() + response.Message = v1BucketsGET.Message + response.SetStatusCode(v1BucketsGET.StatusCode()) + + return c.Render(response.StatusCode(), "documentation-html", response) + } + + //TODO check v1BucketsGET StatusCode and Error + + for bucket, displayName := range v1BucketsGET.Data.Buckets { + // TODO move call to dedicated API client method + var v1BucketReadResponse ReadBucketResponse + + if err = h.ApiClient.Voki.Unmarshal(http.MethodGet, fmt.Sprintf("/v1/bucket/%s", bucket), nil, true, &v1BucketReadResponse); err != nil { + response.Error = err.Error() + response.Message = "Error during json.Unmarshal /v1/bucket/:bucket" + response.SetStatusCode(http.StatusInternalServerError) + + return c.Render(http.StatusOK, "documentation-html", response) + } + + response.Data.Buckets = append(response.Data.Buckets, models.Bucket{ + Name: bucket, + DisplayName: displayName, + Documents: v1BucketReadResponse.Data.Keys, + }) + } + + sort.SliceStable(response.Data.Buckets, func(i, j int) bool { return response.Data.Buckets[i].Name < response.Data.Buckets[j].Name }) + + response.SetStatusCode(http.StatusOK) + //response.Message = "HandleDocumentation ok" + + // TODO render .Message + return c.Render(http.StatusOK, "documentation-html", response) + //return c.Render(response.StatusCode(), "documentation-html", response.Data.Buckets) +} + +func HandleFormulaires(c echo.Context) error { + return c.Render(http.StatusOK, "formulaires-html", nil) +} + +func (h *WebHandler) HandlePublicDocumentation(c echo.Context) error { + bucket := c.Param("bucket") + document := c.Param("document") + + unescaped, err := url.QueryUnescape(fmt.Sprintf("/v1/bucket/%s/%s", bucket, document)) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"message": "Bad Request"}) + } + + response, err := h.ApiClient.Voki.Call(http.MethodGet, unescaped, nil, true) + if err != nil { + response := voki.ResponseNotFound{} + return c.JSON(response.StatusCode(), response) + } + defer response.Body.Close() + + switch response.StatusCode { + case http.StatusNotFound: + response := voki.ResponseNotFound{} + return c.JSON(response.StatusCode(), response) + case http.StatusInternalServerError: + return c.JSON(http.StatusInternalServerError, map[string]string{"message": "Internal Server Error"}) + } + + 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 { + return c.Render(http.StatusOK, "admin-html", nil) +} + +func (h *WebHandler) HandleAdminDocumentsUpload(c echo.Context) error { + var response HandleAdminDocumentsUploadResponse + + v1BucketsGET, err := h.ApiClient.ListBuckets() + if err != nil { + response.SetStatusCode(v1BucketsGET.StatusCode()) + response.Error = err.Error() + response.Message = v1BucketsGET.Message + + return c.Render(response.StatusCode(), "admin-upload-html", nil) + } + + for bucketName, displayName := range v1BucketsGET.Data.Buckets { + response.Data.Buckets = append(response.Data.Buckets, models.Bucket{ + Name: bucketName, + DisplayName: displayName, + }) + } + response.SetStatusCode(http.StatusOK) + + return c.Render(response.StatusCode(), "admin-upload-html", response) +} + +func (h *WebHandler) HandleAdminDocumentsUploadPOST(c echo.Context) error { + var request CreateDocumentsRequest + var response HandleAdminDocumentsUploadResponse + + v1BucketsGET, err := h.ApiClient.ListBuckets() + if err != nil { + response.SetStatusCode(v1BucketsGET.StatusCode()) + response.Message = v1BucketsGET.Message + response.Error = err.Error() + + return c.Render(response.StatusCode(), "admin-upload-html", response) + } + + for bucketName, displayName := range v1BucketsGET.Data.Buckets { + response.Data.Buckets = append(response.Data.Buckets, models.Bucket{ + Name: bucketName, + DisplayName: displayName, + }) + } + + request.Params.Bucket = c.FormValue("bucket") + + form, err := c.MultipartForm() + if err != nil { + response.SetStatusCode(http.StatusBadRequest) + response.Message = "Impossible de téléverser" + response.Error = err.Error() + + return c.Render(response.StatusCode(), "admin-upload-html", response) + } + if form == nil { + response.SetStatusCode(http.StatusInternalServerError) + response.Message = "Formulaire pointe vers une addresse mémoire nulle" + response.Error = "Formulaire pointe vers une addresse mémoire nulle" + + return c.Render(response.StatusCode(), "admin-upload-html", response) + } + + if len(form.File) == 0 { + response.SetStatusCode(http.StatusBadRequest) + response.Message = "Veuillez sélectionner au moins 1 fichier à téléverser" + response.Error = "Input 'documents' ne contient aucun fichier" + + return c.Render(response.StatusCode(), "admin-upload-html", response) + } + + for inputName, inputFileHeaders := range form.File { + if inputName == "documents" { + request.Data.Documents = inputFileHeaders + break + } + } + + if request.Data.Documents == nil { + response.SetStatusCode(http.StatusBadRequest) + response.Message = "Impossible d'obtenir les documents depuis le formulaire" + response.Error = "Impossible d'obtenir les documents depuis le formulaire" + + return c.Render(response.StatusCode(), "admin-upload-html", response) + } + + uploadDocumentsResponse, err := h.ApiClient.UploadDocuments(request.Params.Bucket, request.Data.Documents...) + if err != nil { + //TODO figure out pourquoi `err` n'est jamais `nil` + response.SetStatusCode(uploadDocumentsResponse.StatusCode()) + response.Message = uploadDocumentsResponse.Message + response.Error = fmt.Sprintf("%s. Détails: %s", err.Error(), uploadDocumentsResponse.Error) + /* + response.SetStatusCode(http.StatusInternalServerError) + response.Message = fmt.Sprintf("api.(*API).UploadDocuments: %s", err) + response.Error = err.Error() + */ + + return c.Render(response.StatusCode(), "admin-upload-html", response) + } + + //TODO figure out pourquoi on se rend jamais ici + + // Format response + var info, status string + + for i, document := range uploadDocumentsResponse.Data.Documents { + info = fmt.Sprintf("%s[%d] /public/documentation/%s/%s (%dk) ok\n", + info, i, uploadDocumentsResponse.Data.Bucket, document.Key, document.Size) + } + + status = uploadDocumentsResponse.Message + if errMsg := uploadDocumentsResponse.Error; errMsg != "" { + status = fmt.Sprintf("%s. Erreur: %s", status, errMsg) + } + + response.SetStatusCode(http.StatusOK) + response.Message = fmt.Sprintf("%s \n %s", status, info) + + return c.Render(response.StatusCode(), "admin-upload-html", response) +} diff --git a/webhandler/webhandler.go b/webhandler/webhandler.go deleted file mode 100644 index 082aff2..0000000 --- a/webhandler/webhandler.go +++ /dev/null @@ -1,254 +0,0 @@ -package webhandler - -import ( - "fmt" - "io" - "net/http" - "net/url" - "sort" - - "codeberg.org/vlbeaudoin/voki/v3" - "git.agecem.com/agecem/agecem-org/api" - "git.agecem.com/agecem/agecem-org/apirequest" - "git.agecem.com/agecem/agecem-org/models" - "github.com/labstack/echo/v4" -) - -type WebHandler struct { - ApiClient *api.API -} - -func HandleIndex(c echo.Context) error { - return c.Render(http.StatusOK, "index-html", nil) -} - -/* -func HandleAPropos(c echo.Context) error { - return c.Render(http.StatusOK, "a-propos-html", nil) -} -*/ - -/* -func HandleActualite(c echo.Context) error { - return c.Render(http.StatusOK, "actualite-html", nil) -} -*/ - -/* -func HandleActualiteArticle(c echo.Context) error { - article := c.Param("article") - return c.String(http.StatusOK, fmt.Sprintf("Article: %s", article)) -} -*/ - -func HandleVieEtudiante(c echo.Context) error { - return c.Render(http.StatusOK, "vie-etudiante-html", nil) -} - -func HandleVieEtudianteOrganisme(c echo.Context) error { - organisme := c.Param("organisme") - return c.String(http.StatusOK, fmt.Sprintf("Organisme: %s", organisme)) -} - -func (h *WebHandler) HandleDocumentation(c echo.Context) error { - var response HandleDocumentationResponse - - v1BucketsGET, err := h.ApiClient.ListBuckets() - if err != nil { - response.Error = err.Error() - response.Message = v1BucketsGET.Message - response.SetStatusCode(v1BucketsGET.StatusCode()) - - return c.Render(response.StatusCode(), "documentation-html", response) - } - - //TODO check v1BucketsGET StatusCode and Error - - for bucket, displayName := range v1BucketsGET.Data.Buckets { - // TODO move call to dedicated API client method - var v1BucketReadResponse V1BucketGET - - if err = h.ApiClient.Voki.Unmarshal(http.MethodGet, fmt.Sprintf("/v1/bucket/%s", bucket), nil, true, &v1BucketReadResponse); err != nil { - response.Error = err.Error() - response.Message = "Error during json.Unmarshal /v1/bucket/:bucket" - response.SetStatusCode(http.StatusInternalServerError) - - return c.Render(http.StatusOK, "documentation-html", response) - } - - response.Data.Buckets = append(response.Data.Buckets, models.Bucket{ - Name: bucket, - DisplayName: displayName, - Documents: v1BucketReadResponse.Data.Keys, - }) - } - - sort.SliceStable(response.Data.Buckets, func(i, j int) bool { return response.Data.Buckets[i].Name < response.Data.Buckets[j].Name }) - - response.SetStatusCode(http.StatusOK) - //response.Message = "HandleDocumentation ok" - - // TODO render .Message - return c.Render(http.StatusOK, "documentation-html", response) - //return c.Render(response.StatusCode(), "documentation-html", response.Data.Buckets) -} - -func HandleFormulaires(c echo.Context) error { - return c.Render(http.StatusOK, "formulaires-html", nil) -} - -func (h *WebHandler) HandlePublicDocumentation(c echo.Context) error { - bucket := c.Param("bucket") - document := c.Param("document") - - unescaped, err := url.QueryUnescape(fmt.Sprintf("/v1/bucket/%s/%s", bucket, document)) - if err != nil { - return c.JSON(http.StatusBadRequest, map[string]string{"message": "Bad Request"}) - } - - response, err := h.ApiClient.Voki.Call(http.MethodGet, unescaped, nil, true) - if err != nil { - response := voki.ResponseNotFound{} - return c.JSON(response.StatusCode(), response) - } - defer response.Body.Close() - - switch response.StatusCode { - case http.StatusNotFound: - response := voki.ResponseNotFound{} - return c.JSON(response.StatusCode(), response) - case http.StatusInternalServerError: - return c.JSON(http.StatusInternalServerError, map[string]string{"message": "Internal Server Error"}) - } - - 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 { - return c.Render(http.StatusOK, "admin-html", nil) -} - -func (h *WebHandler) HandleAdminDocumentsUpload(c echo.Context) error { - var response HandleAdminDocumentsUploadResponse - - v1BucketsGET, err := h.ApiClient.ListBuckets() - if err != nil { - response.SetStatusCode(v1BucketsGET.StatusCode()) - response.Error = err.Error() - response.Message = v1BucketsGET.Message - - return c.Render(response.StatusCode(), "admin-upload-html", nil) - } - - for bucketName, displayName := range v1BucketsGET.Data.Buckets { - response.Data.Buckets = append(response.Data.Buckets, models.Bucket{ - Name: bucketName, - DisplayName: displayName, - }) - } - response.SetStatusCode(http.StatusOK) - - return c.Render(response.StatusCode(), "admin-upload-html", response) -} - -func (h *WebHandler) HandleAdminDocumentsUploadPOST(c echo.Context) error { - var request apirequest.CreateDocumentsResponse - var response HandleAdminDocumentsUploadResponse - - v1BucketsGET, err := h.ApiClient.ListBuckets() - if err != nil { - response.SetStatusCode(v1BucketsGET.StatusCode()) - response.Message = v1BucketsGET.Message - response.Error = err.Error() - - return c.Render(response.StatusCode(), "admin-upload-html", response) - } - - for bucketName, displayName := range v1BucketsGET.Data.Buckets { - response.Data.Buckets = append(response.Data.Buckets, models.Bucket{ - Name: bucketName, - DisplayName: displayName, - }) - } - - request.Params.Bucket = c.FormValue("bucket") - - form, err := c.MultipartForm() - if err != nil { - response.SetStatusCode(http.StatusBadRequest) - response.Message = "Impossible de téléverser" - response.Error = err.Error() - - return c.Render(response.StatusCode(), "admin-upload-html", response) - } - if form == nil { - response.SetStatusCode(http.StatusInternalServerError) - response.Message = "Formulaire pointe vers une addresse mémoire nulle" - response.Error = "Formulaire pointe vers une addresse mémoire nulle" - - return c.Render(response.StatusCode(), "admin-upload-html", response) - } - - if len(form.File) == 0 { - response.SetStatusCode(http.StatusBadRequest) - response.Message = "Veuillez sélectionner au moins 1 fichier à téléverser" - response.Error = "Input 'documents' ne contient aucun fichier" - - return c.Render(response.StatusCode(), "admin-upload-html", response) - } - - for inputName, inputFileHeaders := range form.File { - if inputName == "documents" { - request.Data.Documents = inputFileHeaders - break - } - } - - if request.Data.Documents == nil { - response.SetStatusCode(http.StatusBadRequest) - response.Message = "Impossible d'obtenir les documents depuis le formulaire" - response.Error = "Impossible d'obtenir les documents depuis le formulaire" - - return c.Render(response.StatusCode(), "admin-upload-html", response) - } - - uploadDocumentsResponse, err := h.ApiClient.UploadDocuments(request.Params.Bucket, request.Data.Documents...) - if err != nil { - //TODO figure out pourquoi `err` n'est jamais `nil` - response.SetStatusCode(uploadDocumentsResponse.StatusCode()) - response.Message = uploadDocumentsResponse.Message - response.Error = fmt.Sprintf("%s. Détails: %s", err.Error(), uploadDocumentsResponse.Error) - /* - response.SetStatusCode(http.StatusInternalServerError) - response.Message = fmt.Sprintf("api.(*API).UploadDocuments: %s", err) - response.Error = err.Error() - */ - - return c.Render(response.StatusCode(), "admin-upload-html", response) - } - - //TODO figure out pourquoi on se rend jamais ici - - // Format response - var info, status string - - for i, document := range uploadDocumentsResponse.Data.Documents { - info = fmt.Sprintf("%s[%d] /public/documentation/%s/%s (%dk) ok\n", - info, i, uploadDocumentsResponse.Data.Bucket, document.Key, document.Size) - } - - status = uploadDocumentsResponse.Message - if errMsg := uploadDocumentsResponse.Error; errMsg != "" { - status = fmt.Sprintf("%s. Erreur: %s", status, errMsg) - } - - response.SetStatusCode(http.StatusOK) - response.Message = fmt.Sprintf("%s \n %s", status, info) - - return c.Render(response.StatusCode(), "admin-upload-html", response) -}