diff --git a/Dockerfile b/Dockerfile index c8f609b..7497889 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.21.1 as build +FROM golang:1.21.0 as build LABEL author="Victor Lacasse-Beaudoin " @@ -12,17 +12,13 @@ ADD cmd/ cmd/ ADD api/ api/ -ADD api_handlers/ api_handlers/ - ADD config/ config/ ADD media/ media/ -ADD models/ models/ - ADD templates/ templates/ -ADD web_handlers/ web_handlers/ +ADD serverhandlers/ serverhandlers/ RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o agecem-org . diff --git a/README.md b/README.md index 88a3600..ef59413 100644 --- a/README.md +++ b/README.md @@ -44,21 +44,3 @@ Voir les logs des containers Détruire les containers `$ docker-compose down` - -### Exemple de développement sans base de données ou docker - -Pour un environnement simplifié sans minio ou docker, seul le toolchain `go` devrait être nécessaire au démarrage du serveur. - -Pour une exécution sans installation permanente, veuillez utiliser: - -`$ go run . server` - -Si nécessaire, un fichier de config peut être déposé dans `$HOME/.agecem-org.yaml` ou spécifié tel que: - -`$ go run . server --config agecem-org.yaml` - -où `agecem-org.yaml` doit être remplacé par le fichier de config désiré. - -Pour un exemple de fichier de config en format JSON, voir le résultat de: - -`go run . config` diff --git a/api/api.go b/api/api.go index 6ba2d36..42211b6 100644 --- a/api/api.go +++ b/api/api.go @@ -6,12 +6,12 @@ import ( "errors" "fmt" "io" + "io/ioutil" "mime/multipart" "net/http" "net/url" "git.agecem.com/agecem/agecem-org/config" - "git.agecem.com/agecem/agecem-org/models" "github.com/spf13/viper" ) @@ -30,9 +30,17 @@ type APIOptions struct { Password string } -// NewApiClientFromViper returns a pointer to a new API object, -// provided the configuration options are managed by -// https://git.agecem.com/agecem/agecem-org/config +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 NewApiClientFromViper() (*API, error) { var config config.Config @@ -99,7 +107,7 @@ func (a *API) Call(method, route string) ([]byte, error) { defer response.Body.Close() - body, err := io.ReadAll(response.Body) + body, err := ioutil.ReadAll(response.Body) if err != nil { return nil, err } @@ -127,7 +135,7 @@ func (a *API) Call(method, route string) ([]byte, error) { defer resp.Body.Close() // Read Response Body - respBody, err := io.ReadAll(resp.Body) + respBody, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } @@ -138,8 +146,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) (models.V1DocumentCreateResponse, error) { - var response models.V1DocumentCreateResponse +func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) (UploadDocumentResponse, error) { endpoint := fmt.Sprintf("%s://%s:%d", a.Protocol, a.Host, @@ -155,34 +162,34 @@ 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 response, fmt.Errorf("UploadDocument#file_header.Open: %s", err) + return UploadDocumentResponse{}, fmt.Errorf("UploadDocument#file_header.Open: %s", err) } defer file.Close() filename_processed, err := url.QueryUnescape(file_header.Filename) if err != nil { - return response, fmt.Errorf("UploadDocument#url.QueryUnescape: %s", err) + return UploadDocumentResponse{}, fmt.Errorf("UploadDocument#url.QueryUnescape: %s", err) } part, err := writer.CreateFormFile("document", filename_processed) if err != nil { - return response, 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 response, fmt.Errorf("UploadDocument#io.Copy: %s", err) + return UploadDocumentResponse{}, fmt.Errorf("UploadDocument#io.Copy: %s", err) } err = writer.Close() if err != nil { - return response, 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, current_url, body) if err != nil { - return response, fmt.Errorf("UploadDocument#http.NewRequest: %s", err) + return UploadDocumentResponse{}, fmt.Errorf("UploadDocument#http.NewRequest: %s", err) } req.Header.Set("Content-Type", writer.FormDataContentType()) @@ -195,12 +202,15 @@ func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) ( client := &http.Client{} resp, err := client.Do(req) if err != nil { - return response, fmt.Errorf("UploadDocument#client.Do: %s", err) + return UploadDocumentResponse{}, fmt.Errorf("UploadDocument#client.Do: %s", err) } defer resp.Body.Close() - err = json.NewDecoder(resp.Body).Decode(&response) - return response, err + // Handle the response + var res UploadDocumentResponse + + json.NewDecoder(resp.Body).Decode(&res) + return res, nil } // CallWithData takes data and returns a string representing a response body. @@ -271,17 +281,3 @@ func (a *API) CallWithData(method, route string, data []byte) (string, error) { //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 -} diff --git a/api_handlers/api_handlers.go b/api_handlers/api_handlers.go deleted file mode 100644 index 8ce859f..0000000 --- a/api_handlers/api_handlers.go +++ /dev/null @@ -1,313 +0,0 @@ -package api_handlers - -import ( - "context" - "net/http" - "sort" - - "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" -) - -type V1Handler struct { - Config config.Config - MediaClient *media.MediaClient -} - -// API Handlers - -// HandleV1 affiche les routes accessibles. -// Les routes sont triées selon .Path, pour les rendre plus facilement navigables. -func (h *V1Handler) HandleV1(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) -} - -// 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 - - new_buckets, err := h.MediaClient.Seed() - response.Data.Buckets = new_buckets - if err != nil { - response.StatusCode = 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.StatusCode = http.StatusOK - - return c.JSON(response.StatusCode, response) -} - -// HandleV1BucketList affiche les buckets permis par server.documents.buckets, qui existent. -func (h *V1Handler) HandleV1BucketList(c echo.Context) error { - var response models.V1BucketListResponse - - 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.StatusCode = http.StatusInternalServerError - response.Message = "Error during minio#BucketExists" - // response.Error = err.Error() - - return c.JSON(response.StatusCode, response) - } - - if exists { - buckets[bucket_name] = bucket_display_name - } - } - - response.StatusCode = http.StatusOK - response.Message = "Buckets list successful" - response.Data.Buckets = buckets - - return c.JSON(response.StatusCode, response) -} - -func (h *V1Handler) HandleV1BucketRead(c echo.Context) error { - var response models.V1BucketReadResponse - - bucket := c.Param("bucket") - - allowed := false - for bucket_allowed := range h.Config.Server.Documents.Buckets { - if bucket == bucket_allowed { - allowed = true - } - } - - if !allowed { - return c.JSON(models.NotFoundResponse()) - } - - ctx, cancel := context.WithCancel(context.Background()) - - defer cancel() - - exists, err := h.MediaClient.MinioClient.BucketExists(ctx, bucket) - if err != nil { - response.StatusCode = http.StatusInternalServerError - response.Message = "Error during minio#BucketExists" - response.Error = err.Error() - - return c.JSON(response.StatusCode, response) - } - - if !exists { - return c.JSON(models.NotFoundResponse()) - } - - objectCh := h.MediaClient.MinioClient.ListObjects(ctx, bucket, minio.ListObjectsOptions{}) - for object := range objectCh { - if object.Err != nil { - response.StatusCode = http.StatusInternalServerError - response.Message = "Error during minio#ListObjects" - //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.StatusCode = http.StatusOK - response.Message = "V1BucketRead ok" - - return c.JSON(response.StatusCode, response) -} - -// 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 - - bucket := c.Param("bucket") - - form_file, err := c.FormFile("document") - if err != nil { - response.StatusCode = 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 bucket == bucket_allowed { - allowed = true - } - } - - if !allowed { - return c.JSON(models.NotFoundResponse()) - } - - ctx, cancel := context.WithCancel(context.Background()) - - defer cancel() - - src, err := form_file.Open() - if err != nil { - response.StatusCode = 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, bucket, form_file.Filename, src, form_file.Size, minio.PutObjectOptions{ - ContentType: form_file.Header.Get("Content-Type"), - }) - if err != nil { - response.StatusCode = http.StatusInternalServerError - response.Message = "Error during minio#PutObject" - //response.Error = err.Error() - - return c.JSON(response.StatusCode, response) - } - - response.StatusCode = 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) -} - -// HandleV1DocumentRead permet de lire le contenu d'un fichier et protentiellement de le télécharger -func (h *V1Handler) HandleV1DocumentRead(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 { - return c.JSON(models.NotFoundResponse()) - } - - 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 { - return c.JSON(models.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(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) -} - -// 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()) -} - -// HandleV1DocumentDelete permet de supprimer un object -func (h *V1Handler) HandleV1DocumentDelete(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 { - return c.JSON(models.NotFoundResponse()) - } - - 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 { - return c.JSON(models.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(http.StatusInternalServerError, map[string]interface{}{ - "message": "Error during minio#StatObject", - }) - } - - //TODO Add error validation - _ = document_info - - err = h.MediaClient.MinioClient.RemoveObject(ctx, bucket, document, minio.RemoveObjectOptions{}) - if err != nil { - return c.JSON(http.StatusInternalServerError, map[string]string{ - "message": "Error during minio#RemoveObject", - }) - } - - return c.JSON(http.StatusOK, map[string]string{ - "message": "Document deleted", - }) -} diff --git a/cmd/server.go b/cmd/server.go index aac8313..e37b48f 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -5,8 +5,10 @@ package cmd import ( "crypto/subtle" + "encoding/json" "fmt" "log" + "sort" "embed" "html/template" @@ -17,12 +19,11 @@ import ( "github.com/spf13/viper" "git.agecem.com/agecem/agecem-org/api" - "git.agecem.com/agecem/agecem-org/api_handlers" "git.agecem.com/agecem/agecem-org/config" "git.agecem.com/agecem/agecem-org/media" "git.agecem.com/agecem/agecem-org/public" + "git.agecem.com/agecem/agecem-org/serverhandlers" "git.agecem.com/agecem/agecem-org/templates" - "git.agecem.com/agecem/agecem-org/web_handlers" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" ) @@ -48,18 +49,17 @@ var serverCmd = &cobra.Command{ } mediaClient, err := media.NewMediaClientFromViper() - switch err != nil { - case true: - log.Printf("media.NewMediaClientFromViper error: %s", err) - case false: - new_buckets, err := mediaClient.Seed() - if err != nil { - log.Printf("(*media.MediaClient).Seed error: %s", err) - } else { - log.Printf("Seeded %d buckets.\n", len(new_buckets)) - } + if err != nil { + log.Fatal(err) } + new_buckets, err := mediaClient.Seed() + if err != nil { + log.Fatal(err) + } + + log.Printf("Seeded %d buckets.\n", len(new_buckets)) + RunServer() }, } @@ -199,69 +199,52 @@ func RunServer() { } // API Routes - mediaClient, err := media.NewMediaClientFromViper() - if err != nil { - log.Fatal("Error during NewMediaClientFromViper for API handlers") - } - v1Handler := api_handlers.V1Handler{ - Config: cfg, - MediaClient: mediaClient, - } + groupV1.GET("", serverhandlers.HandleV1) - groupV1.GET("", v1Handler.HandleV1) + groupV1.POST("/seed", serverhandlers.HandleV1Seed) - groupV1.POST("/seed", v1Handler.HandleV1Seed) + groupV1.GET("/bucket", serverhandlers.HandleV1BucketList) - groupV1.GET("/bucket", v1Handler.HandleV1BucketList) + groupV1.GET("/bucket/:bucket", serverhandlers.HandleV1BucketRead) - groupV1.GET("/bucket/:bucket", v1Handler.HandleV1BucketRead) + groupV1.POST("/bucket/:bucket", serverhandlers.HandleV1DocumentCreate) - groupV1.POST("/bucket/:bucket", v1Handler.HandleV1DocumentCreate) + groupV1.GET("/bucket/:bucket/:document", serverhandlers.HandleV1DocumentRead) - groupV1.GET("/bucket/:bucket/:document", v1Handler.HandleV1DocumentRead) + groupV1.PUT("/bucket/:bucket/:document", serverhandlers.HandleV1DocumentUpdate) - groupV1.PUT("/bucket/:bucket/:document", v1Handler.HandleV1DocumentUpdate) - - groupV1.DELETE("/bucket/:bucket/:document", v1Handler.HandleV1DocumentDelete) + groupV1.DELETE("/bucket/:bucket/:document", serverhandlers.HandleV1DocumentDelete) // HTML Routes - apiClient, err := api.NewApiClientFromViper() - if err != nil { - log.Fatal("Error during NewMediaClientFromViper for API handlers") - } - webHandler := web_handlers.WebHandler{ - ApiClient: apiClient, - } + e.GET("/", handleIndex) - e.GET("/", web_handlers.HandleIndex) + //e.GET("/a-propos", handleAPropos) - //e.GET("/a-propos", web_handlers.HandleAPropos) + //e.GET("/actualite", handleActualite) - //e.GET("/actualite", web_handlers.HandleActualite) + //e.GET("/actualite/:article", handleActualiteArticle) - //e.GET("/actualite/:article", web_handlers.HandleActualiteArticle) + e.GET("/vie-etudiante", handleVieEtudiante) - e.GET("/vie-etudiante", web_handlers.HandleVieEtudiante) + e.GET("/vie-etudiante/:organisme", handleVieEtudianteOrganisme) - e.GET("/vie-etudiante/:organisme", web_handlers.HandleVieEtudianteOrganisme) + e.GET("/documentation", handleDocumentation) - e.GET("/documentation", webHandler.HandleDocumentation) - - e.GET("/formulaires", web_handlers.HandleFormulaires) + e.GET("/formulaires", handleFormulaires) // Public Routes - e.GET("/public/documentation/:bucket/:document", webHandler.HandlePublicDocumentation) + e.GET("/public/documentation/:bucket/:document", handlePublicDocumentation) // Admin Routes - groupAdmin.GET("", web_handlers.HandleAdmin) + groupAdmin.GET("", handleAdmin) - groupAdmin.GET("/documents/upload", webHandler.HandleAdminDocumentsUpload) + groupAdmin.GET("/documents/upload", handleAdminDocumentsUpload) - groupAdmin.POST("/documents/upload", webHandler.HandleAdminDocumentsUploadPOST) + groupAdmin.POST("/documents/upload", handleAdminDocumentsUploadPOST) e.Logger.Fatal(e.Start( fmt.Sprintf(":%d", cfg.Server.Port))) @@ -270,3 +253,248 @@ func RunServer() { func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error { return t.templates.ExecuteTemplate(w, name, data) } + +// HTML Handlers + +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 handleDocumentation(c echo.Context) error { + client, err := api.NewApiClientFromViper() + if err != nil { + return c.Render(http.StatusInternalServerError, "documentation-html", nil) + } + + result, err := client.Call(http.MethodGet, "/v1/bucket") + if err != nil { + return c.Render(http.StatusInternalServerError, "documentation-html", nil) + } + + var buckets map[string]string + + err = json.Unmarshal(result, &buckets) + if err != nil { + return c.Render(http.StatusInternalServerError, "documentation-html", nil) + } + + type Bucket struct { + Name string + DisplayName string + Documents []string + } + + var data []Bucket + + for bucket, displayName := range buckets { + 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(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, + DisplayName: displayName, + Documents: documents, + }) + } + + sort.SliceStable(data, func(i, j int) bool { return data[i].Name < data[j].Name }) + + return c.Render(http.StatusOK, "documentation-html", data) +} + +func handleFormulaires(c echo.Context) error { + return c.Render(http.StatusOK, "formulaires-html", nil) +} + +func handlePublicDocumentation(c echo.Context) error { + client, err := api.NewApiClientFromViper() + if err != nil { + return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) + } + + bucket := c.Param("bucket") + document := c.Param("document") + + result, err := client.Call(http.MethodGet, fmt.Sprintf("/v1/bucket/%s/%s", bucket, document)) + if err != nil { + return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) + } + + // Check if result can fit inside a map containing a message + var result_map map[string]string + + err = json.Unmarshal(result, &result_map) + if err == nil { + return c.JSON(http.StatusBadRequest, result_map) + } + + return c.Blob(http.StatusOK, "application/octet-stream", result) +} + +func handleAdmin(c echo.Context) error { + return c.Render(http.StatusOK, "admin-html", nil) +} + +func handleAdminDocumentsUpload(c echo.Context) error { + client, err := api.NewApiClientFromViper() + if err != nil { + return c.Render(http.StatusInternalServerError, "documentation-html", nil) + } + + result, err := client.Call(http.MethodGet, "/v1/bucket") + if err != nil { + return c.Render(http.StatusInternalServerError, "documentation-html", nil) + } + + var buckets map[string]string + + err = json.Unmarshal(result, &buckets) + if err != nil { + return c.Render(http.StatusInternalServerError, "documentation-html", nil) + } + + type Bucket struct { + Name string + DisplayName string + Documents []string + } + + var data struct { + Buckets []Bucket + Message string + } + + for bucketName, displayName := range buckets { + data.Buckets = append(data.Buckets, Bucket{ + Name: bucketName, + DisplayName: displayName, + }) + } + + return c.Render(http.StatusOK, "admin-upload-html", data) +} + +func handleAdminDocumentsUploadPOST(c echo.Context) error { + type Bucket struct { + Name string + DisplayName string + Documents []string + } + + var data struct { + Buckets []Bucket + Message string + } + + client, err := api.New(cfg.Server.Api.Protocol, cfg.Server.Api.Host, cfg.Server.Port, api.APIOptions{ + KeyAuth: cfg.Server.Api.Auth, + Key: cfg.Server.Api.Key, + BasicAuth: cfg.Server.Admin.Auth, + Username: cfg.Server.Admin.Username, + Password: cfg.Server.Admin.Password, + }) + if err != nil { + data.Message = fmt.Sprintf("handleAdminDocumentsUploadPOST#api.New: %s", err) + return c.Render(http.StatusInternalServerError, "admin-upload-html", data) + } + + result, err := client.Call(http.MethodGet, "/v1/bucket") + if err != nil { + data.Message = "Error during GET /v1/bucket" + return c.Render(http.StatusInternalServerError, "documentation-html", data) + } + + var buckets map[string]string + + err = json.Unmarshal(result, &buckets) + if err != nil { + return c.Render(http.StatusInternalServerError, "documentation-html", nil) + } + + for bucketName, displayName := range buckets { + data.Buckets = append(data.Buckets, Bucket{ + Name: bucketName, + DisplayName: displayName, + }) + } + + bucket := c.FormValue("bucket") + + document, err := c.FormFile("document") + if err != nil { + data.Message = fmt.Sprintf("handleAdminDocumentsUploadPOST#c.FormFile: %s", err) + return c.Render(http.StatusBadRequest, "admin-upload-html", data) + } + + response, err := client.UploadDocument(bucket, document) + if err != nil { + data.Message = fmt.Sprintf("handleAdminDocumentsUploadPOST#client.UploadDocument: %s", err) + return c.Render(http.StatusInternalServerError, "admin-upload-html", data) + } + + // Format response + var info, status string + + info = fmt.Sprintf("[%.0f] /public/documentation/%s/%s", response.Info.Size, response.Info.Bucket, response.Info.Object) + + status = response.Message + + data.Message = fmt.Sprintf("%s - %s", status, info) + + return c.Render(http.StatusOK, "admin-upload-html", data) +} diff --git a/models/models.go b/models/models.go deleted file mode 100644 index 9ef3c9e..0000000 --- a/models/models.go +++ /dev/null @@ -1,7 +0,0 @@ -package models - -type Bucket struct { - Name string - DisplayName string - Documents []string -} 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/public/css/admin-upload.css b/public/css/admin-upload.css deleted file mode 100644 index c790b9a..0000000 --- a/public/css/admin-upload.css +++ /dev/null @@ -1,120 +0,0 @@ -.adminUploadForm { - font-family: 'Poppins'; - display: flex; - flex-flow: column; - align-items: center; -} - -.formContent { - display: flex; - flex-flow: column; - -} - -.formSelectDiv { - display: flex; - flex-flow: column; - margin: 20px; -} - -.formLabel { - font-family: 'Poppins'; - font-size: 0.875rem; - font-weight: 600; - margin: 0; - padding-top: 10px; - padding-bottom: 10px; - color: #394596; -} - -.formSelect { - font-family: 'Poppins'; - font-size: 0.875rem; - font-weight: 400; - border: 1px #C4C4C4 solid; - padding: 10px; - -} - -/*La flèche de l'élément*/ -.formSelect:after { - color: #000 -} - -.formOption { - font-family: 'Poppins'; - font-size: 0.875rem; - font-weight: 400; - margin-top: 5px; - margin-bottom: 5px; -} - -.formOption:hover { - background-color: #C4C4C4; -} - -.formDocUploadDiv { - display: flex; - flex-flow: column; - margin: 20px; -} - -.formDocUpload { - font-family: 'Poppins'; - font-size: 0.875rem; - font-weight: 500; -} - -.formDocUpload::file-selector-button { - font-family: 'Poppins'; - font-size: 0.875rem; - font-weight: 400; - background-color: #FF563C; - padding: 7px; - color: #fff; - border: none; - cursor: pointer; - margin-right: 15px; -} - -.formSubmit { - font-family: 'Poppins'; - font-size: 1rem; - font-weight: 500; - background-color: #FF563C; - padding: 10px; - color: #fff; - border: none; - cursor: pointer; - margin: 20px; -} - -.confirmationMessage { - font-family: 'Poppins'; - font-size: 0.75rem; - font-weight: 500; - text-align: center; - margin: 10px; - padding: 5px; - background-color: #C4C4C4; -} - -@media screen and (min-width: 768px) { - - .formContent { - display: flex; - flex-flow: row; - } - -} - -@media screen and (min-width: 1140px) { - - .confirmationMessage { - font-size: 1rem; - font-weight: 500; - margin: 20px; - padding: 10px; - } - -} \ No newline at end of file diff --git a/public/css/snackbar.css b/public/css/snackbar.css deleted file mode 100644 index 724359a..0000000 --- a/public/css/snackbar.css +++ /dev/null @@ -1,43 +0,0 @@ -.snackbar { - position: fixed; - bottom: 0; - background-color: #FF563C; - color: #fff; - width: 100%; - padding: 5px 0; - opacity: 0.8; -} - -.snackbar-is-closed { - display: none; -} - -.snackbarWrapper { - display: flex; - flex-flow: row; - align-items: center; - justify-content: space-between; - line-height: 100%; -} - -span { - margin: 10px 10px 10px 0; - font-size: 0.75rem; - /*12px*/ - font-family: 'Poppins'; - font-weight: 600; - /*semi-bold*/ -} - -@media screen and (min-width: 375px) { - span { - font-size: 1rem; - /*16px*/ - } -} - -.snackbarFermer { - height: 30px; - width: 30px; - cursor: pointer; -} diff --git a/public/icones/fermer.svg b/public/icones/fermer.svg deleted file mode 100644 index 98ee73a..0000000 --- a/public/icones/fermer.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/serverhandlers/serverhandlers.go b/serverhandlers/serverhandlers.go new file mode 100644 index 0000000..6aca6d1 --- /dev/null +++ b/serverhandlers/serverhandlers.go @@ -0,0 +1,381 @@ +package serverhandlers + +import ( + "context" + "net/http" + "sort" + + "git.agecem.com/agecem/agecem-org/config" + "git.agecem.com/agecem/agecem-org/media" + "github.com/labstack/echo/v4" + "github.com/minio/minio-go/v7" + "github.com/spf13/viper" +) + +// API Handlers + +// HandleV1 affiche les routes accessibles. +// Les routes sont triées selon .Path, pour les rendre plus facilement navigables. +func HandleV1(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) +} + +// 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 HandleV1Seed(c echo.Context) error { + mediaClient, err := media.NewMediaClientFromViper() + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during media.NewMediaClientFromViper()", + "error": err.Error(), + }) + } + + new_buckets, err := mediaClient.Seed() + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during mediaClient.Seed()", + "error": err.Error(), + }) + } + + var message string + if len(new_buckets) == 0 { + message = "All buckets already exist" + + } else { + message = "Buckets successfully created" + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "message": message, + "buckets": new_buckets, + }) +} + +// HandleV1BucketList affiche les buckets permis par server.documents.buckets, qui existent. +func HandleV1BucketList(c echo.Context) error { + var cfg config.Config + if err := viper.Unmarshal(&cfg); err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "error": err.Error(), + }) + } + + mediaClient, err := media.NewMediaClientFromViper() + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during media.NewMediaClientFromViper()", + "error": err.Error(), + }) + } + + var buckets = make(map[string]string) + + for bucket_name, bucket_display_name := range cfg.Server.Documents.Buckets { + exists, err := mediaClient.MinioClient.BucketExists(context.Background(), bucket_name) + if err != nil { + return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") + } + + if exists { + buckets[bucket_name] = bucket_display_name + } + } + + return c.JSON(http.StatusOK, buckets) +} + +func HandleV1BucketRead(c echo.Context) error { + var cfg config.Config + if err := viper.Unmarshal(&cfg); err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "error": err.Error(), + }) + } + + bucket := c.Param("bucket") + + allowed := false + for bucket_allowed := range cfg.Server.Documents.Buckets { + if bucket == bucket_allowed { + allowed = true + } + } + + 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"}) + } + + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + + mediaClient, err := media.NewMediaClientFromViper() + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during media.NewMediaClientFromViper()", + "error": err.Error(), + }) + } + + exists, err := mediaClient.MinioClient.BucketExists(ctx, bucket) + if err != nil { + return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") + } + + if !exists { + return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) + } + + var keys []string + + objectCh := mediaClient.MinioClient.ListObjects(ctx, bucket, minio.ListObjectsOptions{}) + for object := range objectCh { + if object.Err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during minio#ListObjects", + }) + } + + keys = append(keys, object.Key) + } + + return c.JSON(http.StatusOK, keys) +} + +// HandleV1DocumentCreate permet d'ajouter un object dans un bucket, par multipart/form-data +func HandleV1DocumentCreate(c echo.Context) error { + var cfg config.Config + if err := viper.Unmarshal(&cfg); err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "error": err.Error(), + }) + } + + bucket := c.Param("bucket") + + form_file, err := c.FormFile("document") + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]interface{}{ + "message": "Error during HandleV1DocumentCreate's echo#Context.FormFile", + "error": err, + }) + } + + allowed := false + for bucket_allowed := range cfg.Server.Documents.Buckets { + if bucket == bucket_allowed { + allowed = true + } + } + + if !allowed { + return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) + } + + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + + mediaClient, err := media.NewMediaClientFromViper() + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during media.NewMediaClientFromViper()", + "error": err.Error(), + }) + } + + src, err := form_file.Open() + if err != nil { + return err + } + defer src.Close() + + /* + 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 := mediaClient.MinioClient.PutObject(ctx, bucket, form_file.Filename, src, form_file.Size, minio.PutObjectOptions{ + ContentType: form_file.Header.Get("Content-Type"), + }) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during minio#PutObject", + }) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "message": "ok", + "info": map[string]interface{}{ + "bucket": info.Bucket, + "key": info.Key, + "size": info.Size, + }, + }) +} + +// HandleV1DocumentRead permet de lire le contenu d'un fichier et protentiellement de le télécharger +func HandleV1DocumentRead(c echo.Context) error { + var cfg config.Config + if err := viper.Unmarshal(&cfg); err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "error": err.Error(), + }) + } + + bucket := c.Param("bucket") + document := c.Param("document") + + allowed := false + for bucket_allowed := range cfg.Server.Documents.Buckets { + if bucket == bucket_allowed { + allowed = true + } + } + + if !allowed { + return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) + } + + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + + mediaClient, err := media.NewMediaClientFromViper() + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during media.NewMediaClientFromViper()", + "error": err.Error(), + }) + } + + bucket_exists, err := mediaClient.MinioClient.BucketExists(ctx, bucket) + if err != nil { + return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") + } + + if !bucket_exists { + return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) + } + + document_info, err := mediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{}) + + if err != nil { + if err.Error() == "The specified key does not exist." { + + return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) + } + + return c.JSON(http.StatusInternalServerError, map[string]interface{}{ + "message": "Error during minio#StatObject", + }) + } + + _ = document_info + + document_object, err := 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) +} + +// HandleV1DocumentUpdate permet de mettre à jour certains champs d'un object, comme le Content-Type ou le Filename +func HandleV1DocumentUpdate(c echo.Context) error { + return c.JSON(http.StatusNotImplemented, map[string]string{ + "message": "Not Implemented", + }) +} + +// HandleV1DocumentDelete permet de supprimer un object +func HandleV1DocumentDelete(c echo.Context) error { + var cfg config.Config + if err := viper.Unmarshal(&cfg); err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "error": err.Error(), + }) + } + bucket := c.Param("bucket") + document := c.Param("document") + + allowed := false + for bucket_allowed := range cfg.Server.Documents.Buckets { + if bucket == bucket_allowed { + allowed = true + } + } + + 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"}) + } + + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + + mediaClient, err := media.NewMediaClientFromViper() + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during media.NewMediaClientFromViper()", + "error": err.Error(), + }) + } + + bucket_exists, err := mediaClient.MinioClient.BucketExists(ctx, bucket) + if err != nil { + return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") + } + + if !bucket_exists { + return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) + } + + document_info, err := mediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{}) + if err != nil { + if err.Error() == "The specified key does not exist." { + + return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) + } + + return c.JSON(http.StatusInternalServerError, map[string]interface{}{ + "message": "Error during minio#StatObject", + }) + } + + //TODO Add error validation + _ = document_info + + err = mediaClient.MinioClient.RemoveObject(ctx, bucket, document, minio.RemoveObjectOptions{}) + if err != nil { + return c.JSON(http.StatusInternalServerError, map[string]string{ + "message": "Error during minio#RemoveObject", + }) + } + + return c.JSON(http.StatusOK, map[string]string{ + "message": "Document deleted", + }) +} diff --git a/templates/html/admin-upload.gohtml b/templates/html/admin-upload.gohtml index 6c92230..c7afd53 100644 --- a/templates/html/admin-upload.gohtml +++ b/templates/html/admin-upload.gohtml @@ -5,31 +5,24 @@ AGECEM {{ template "general-html" }} - {{ template "header-html" }} -
-

Ajouter un document

-
-
-
-

Document à téléverser

- -
-
- - -
-
- -
-

Confirmation: {{ .Message }}

-
+

Upload

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

{{ .Message }}

{{ end }} diff --git a/templates/html/admin.gohtml b/templates/html/admin.gohtml index c424ee4..c596118 100644 --- a/templates/html/admin.gohtml +++ b/templates/html/admin.gohtml @@ -12,7 +12,7 @@

Admin

-
diff --git a/templates/html/documentation.gohtml b/templates/html/documentation.gohtml index ea18bd3..ab74478 100644 --- a/templates/html/documentation.gohtml +++ b/templates/html/documentation.gohtml @@ -12,28 +12,21 @@

Documentation

- {{ if not .Data.Buckets }} -Documentation non-accessible pour l'instant, merci de votre patience - {{ else }} - {{ range .Data.Buckets }} - {{ $bucket_name := .Name }} - {{ $bucket_display_name := .DisplayName }} -

- {{ $bucket_display_name }} -
    - {{ range .Documents }} -
  • {{ . }}
  • - {{ end}} -
-
- {{ end }} + {{ range . }} + {{ $bucket_name := .Name }} + {{ $bucket_display_name := .DisplayName }} +
+ {{ $bucket_display_name }} + +
    + {{ range .Documents }} +
  • {{ . }}
  • + {{ end}} +
+
{{ end }}

-

- {{ .Message }} -

- {{ template "snackbar-html" }} {{ end }} diff --git a/templates/html/index.gohtml b/templates/html/index.gohtml index 7e154a2..dab01aa 100644 --- a/templates/html/index.gohtml +++ b/templates/html/index.gohtml @@ -12,29 +12,7 @@

AGECEM

Association Générale Étudiante du Cégep Édouard-Montpetit

-

- Fondée en 1976, l’Association Étudiante est un organisme sans but lucratif voué à la défense des étudiant·e·s inscrit·e·s à l’enseignement régulier du Campus de Longueuil du Cégep Édouard-Montpetit, qu’iels étudient de jour ou de soir, à temps plein ou à temps partiel. -

-

- Forte d’environ 6000 membres, elle veille à promouvoir un milieu pédagogique sain en se consacrant à les représenter, tant au niveau académique, politique, social, qu’environnemental. -

-
-

Contact

-

Courriel

- permanence@agecem.org -

Téléphone

- (450) 679-7375 -

Addresse de coordination

- 945 Chemin de Chambly, Longueuil, QC J4H 3M6 -

Local

- B-31 -

Réseaux sociaux

-
- {{ template "snackbar-html" }} {{ end }} diff --git a/templates/html/snackbar.gohtml b/templates/html/snackbar.gohtml deleted file mode 100644 index 01c1fd0..0000000 --- a/templates/html/snackbar.gohtml +++ /dev/null @@ -1,15 +0,0 @@ -{{ define "snackbar-html" }} - - -
-
- Ce site web est présentement en construction. - -
-
-{{ end }} \ No newline at end of file diff --git a/templates/html/vie-etudiante.gohtml b/templates/html/vie-etudiante.gohtml index 104df94..893e770 100644 --- a/templates/html/vie-etudiante.gohtml +++ b/templates/html/vie-etudiante.gohtml @@ -10,147 +10,7 @@ {{ template "header-html" }}

Vie étudiante

-

Organismes thématiques

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NomLocalPoste téléphonique
AMEC-0607919
BEAMF-024a5930
CICF-027c
Club AventurierF-011b2730
Équipe SantéF-0112361
Montpetit DonjonC-0672299
MAELF-027b
OGREF-011c5647
Radio
ORGASMEF-027d
SOI
MotDit
-
-

Associations de programme

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NomLocalPoste téléphonique
ADEPTF-0452286
ASI
ATIMF-0412652
AEALCA-125r2873
PAPIF-0232795
TEE
TGEC-0632638
-
-

Comités

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NomLocalPoste téléphonique
CAP
ESPACEF-011d2418
CFEM
ASEGB-06
Comité Mob
EUMC-CEMC-0542356
CÉSI
FriperieF-027a2248
- {{ template "snackbar-html" }} {{ end }} diff --git a/web_handlers/web_handlers.go b/web_handlers/web_handlers.go deleted file mode 100644 index 1dd96e9..0000000 --- a/web_handlers/web_handlers.go +++ /dev/null @@ -1,204 +0,0 @@ -package web_handlers - -import ( - "encoding/json" - "fmt" - "net/http" - "sort" - - "git.agecem.com/agecem/agecem-org/api" - "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 models.HandleDocumentationResponse - - v1BucketListResponse, err := h.ApiClient.ListBuckets() - if err != nil { - response.StatusCode = v1BucketListResponse.StatusCode - response.Message = v1BucketListResponse.Message - response.Error = err.Error() - - return c.Render(response.StatusCode, "documentation-html", response) - } - - //TODO check v1BucketListRespone StatusCode and 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" - 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() - - return c.Render(response.StatusCode, "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.StatusCode = http.StatusOK - //response.Message = "HandleDocumentation ok" - - // TODO render .Message - return c.Render(response.StatusCode, "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") - - result, err := h.ApiClient.Call(http.MethodGet, fmt.Sprintf("/v1/bucket/%s/%s", bucket, document)) - if err != nil { - return c.JSON(models.NotFoundResponse()) - } - - // Check if result can fit inside a map containing a message - var result_map map[string]string - - err = json.Unmarshal(result, &result_map) - if err == nil { - return c.JSON(http.StatusBadRequest, result_map) - } - - return c.Blob(http.StatusOK, "application/octet-stream", result) -} - -func HandleAdmin(c echo.Context) error { - return c.Render(http.StatusOK, "admin-html", nil) -} - -func (h *WebHandler) HandleAdminDocumentsUpload(c echo.Context) error { - var response models.HandleAdminDocumentsUploadResponse - - v1BucketListResponse, err := h.ApiClient.ListBuckets() - if err != nil { - response.StatusCode = v1BucketListResponse.StatusCode - response.Error = err.Error() - response.Message = v1BucketListResponse.Message - - return c.Render(response.StatusCode, "admin-upload-html", nil) - } - - for bucketName, displayName := range v1BucketListResponse.Data.Buckets { - response.Data.Buckets = append(response.Data.Buckets, models.Bucket{ - Name: bucketName, - DisplayName: displayName, - }) - } - response.StatusCode = http.StatusOK - - return c.Render(response.StatusCode, "admin-upload-html", response) -} - -func (h *WebHandler) HandleAdminDocumentsUploadPOST(c echo.Context) error { - var response models.HandleAdminDocumentsUploadResponse - - v1BucketListResponse, err := h.ApiClient.ListBuckets() - if err != nil { - response.StatusCode = v1BucketListResponse.StatusCode - response.Message = v1BucketListResponse.Message - response.Error = err.Error() - - return c.Render(response.StatusCode, "admin-upload-html", response) - } - - for bucketName, displayName := range v1BucketListResponse.Data.Buckets { - response.Data.Buckets = append(response.Data.Buckets, models.Bucket{ - Name: bucketName, - DisplayName: displayName, - }) - } - - bucket := c.FormValue("bucket") - - document, err := c.FormFile("document") - if err != nil { - response.StatusCode = http.StatusBadRequest - response.Message = "Formulaire invalide" - response.Error = err.Error() - - return c.Render(response.StatusCode, "admin-upload-html", response) - } - - uploadDocumentResponse, err := h.ApiClient.UploadDocument(bucket, document) - if err != nil { - response.StatusCode = uploadDocumentResponse.StatusCode - response.Message = uploadDocumentResponse.Message - response.Error = err.Error() - - return c.Render(response.StatusCode, "admin-upload-html", response) - } - - // Format response - var info, status string - - info = fmt.Sprintf("[%d] /public/documentation/%s/%s", uploadDocumentResponse.Data.Size, uploadDocumentResponse.Data.Bucket, uploadDocumentResponse.Data.Key) - - status = uploadDocumentResponse.Message - - response.StatusCode = http.StatusOK - response.Message = fmt.Sprintf("%s - %s", status, info) - - return c.Render(response.StatusCode, "admin-upload-html", response) -}