Merge branch 'main' into fix/affichage-admin-upload
This commit is contained in:
commit
d08e9ffddd
15 changed files with 969 additions and 699 deletions
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.21.0 as build
|
||||
FROM golang:1.21.1 as build
|
||||
|
||||
LABEL author="Victor Lacasse-Beaudoin <vlbeaudoin@agecem.org>"
|
||||
|
||||
|
@ -12,13 +12,17 @@ ADD cmd/ cmd/
|
|||
|
||||
ADD api/ api/
|
||||
|
||||
ADD api_handlers/ api_handlers/
|
||||
|
||||
ADD config/ config/
|
||||
|
||||
ADD media/ media/
|
||||
|
||||
ADD models/ models/
|
||||
|
||||
ADD templates/ templates/
|
||||
|
||||
ADD serverhandlers/ serverhandlers/
|
||||
ADD web_handlers/ web_handlers/
|
||||
|
||||
RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o agecem-org .
|
||||
|
||||
|
|
18
README.md
18
README.md
|
@ -44,3 +44,21 @@ 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`
|
||||
|
|
58
api/api.go
58
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,17 +30,9 @@ type APIOptions struct {
|
|||
Password string
|
||||
}
|
||||
|
||||
type UploadDocumentResponse struct {
|
||||
Info UploadDocumentResponseInfo `json:"info"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type UploadDocumentResponseInfo struct {
|
||||
Bucket string `json:"bucket"`
|
||||
Object string `json:"key"`
|
||||
Size float64 `json:"size"`
|
||||
}
|
||||
|
||||
// NewApiClientFromViper returns a pointer to a new API object,
|
||||
// provided the configuration options are managed by
|
||||
// https://git.agecem.com/agecem/agecem-org/config
|
||||
func NewApiClientFromViper() (*API, error) {
|
||||
var config config.Config
|
||||
|
||||
|
@ -107,7 +99,7 @@ func (a *API) Call(method, route string) ([]byte, error) {
|
|||
|
||||
defer response.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
body, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -135,7 +127,7 @@ func (a *API) Call(method, route string) ([]byte, error) {
|
|||
defer resp.Body.Close()
|
||||
|
||||
// Read Response Body
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -146,7 +138,8 @@ 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) (UploadDocumentResponse, error) {
|
||||
func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) (models.V1DocumentCreateResponse, error) {
|
||||
var response models.V1DocumentCreateResponse
|
||||
endpoint := fmt.Sprintf("%s://%s:%d",
|
||||
a.Protocol,
|
||||
a.Host,
|
||||
|
@ -162,34 +155,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 UploadDocumentResponse{}, fmt.Errorf("UploadDocument#file_header.Open: %s", err)
|
||||
return response, fmt.Errorf("UploadDocument#file_header.Open: %s", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
filename_processed, err := url.QueryUnescape(file_header.Filename)
|
||||
if err != nil {
|
||||
return UploadDocumentResponse{}, fmt.Errorf("UploadDocument#url.QueryUnescape: %s", err)
|
||||
return response, fmt.Errorf("UploadDocument#url.QueryUnescape: %s", err)
|
||||
}
|
||||
|
||||
part, err := writer.CreateFormFile("document", filename_processed)
|
||||
if err != nil {
|
||||
return UploadDocumentResponse{}, fmt.Errorf("UploadDocument#writer.CreateFormFile: %s", err)
|
||||
return response, fmt.Errorf("UploadDocument#writer.CreateFormFile: %s", err)
|
||||
}
|
||||
|
||||
_, err = io.Copy(part, file)
|
||||
if err != nil {
|
||||
return UploadDocumentResponse{}, fmt.Errorf("UploadDocument#io.Copy: %s", err)
|
||||
return response, fmt.Errorf("UploadDocument#io.Copy: %s", err)
|
||||
}
|
||||
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
return UploadDocumentResponse{}, fmt.Errorf("UploadDocument#writer.Close: %s", err)
|
||||
return response, 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 UploadDocumentResponse{}, fmt.Errorf("UploadDocument#http.NewRequest: %s", err)
|
||||
return response, fmt.Errorf("UploadDocument#http.NewRequest: %s", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
|
@ -202,15 +195,12 @@ func (a *API) UploadDocument(bucket string, file_header *multipart.FileHeader) (
|
|||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return UploadDocumentResponse{}, fmt.Errorf("UploadDocument#client.Do: %s", err)
|
||||
return response, fmt.Errorf("UploadDocument#client.Do: %s", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Handle the response
|
||||
var res UploadDocumentResponse
|
||||
|
||||
json.NewDecoder(resp.Body).Decode(&res)
|
||||
return res, nil
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
return response, err
|
||||
}
|
||||
|
||||
// CallWithData takes data and returns a string representing a response body.
|
||||
|
@ -281,3 +271,17 @@ 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
|
||||
}
|
||||
|
|
313
api_handlers/api_handlers.go
Normal file
313
api_handlers/api_handlers.go
Normal file
|
@ -0,0 +1,313 @@
|
|||
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",
|
||||
})
|
||||
}
|
326
cmd/server.go
326
cmd/server.go
|
@ -5,10 +5,8 @@ package cmd
|
|||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
|
||||
"embed"
|
||||
"html/template"
|
||||
|
@ -19,11 +17,12 @@ 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"
|
||||
)
|
||||
|
@ -49,17 +48,18 @@ var serverCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
mediaClient, err := media.NewMediaClientFromViper()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
new_buckets, err := mediaClient.Seed()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Printf("Seeded %d buckets.\n", len(new_buckets))
|
||||
|
||||
RunServer()
|
||||
},
|
||||
}
|
||||
|
@ -199,52 +199,69 @@ func RunServer() {
|
|||
}
|
||||
|
||||
// API Routes
|
||||
mediaClient, err := media.NewMediaClientFromViper()
|
||||
if err != nil {
|
||||
log.Fatal("Error during NewMediaClientFromViper for API handlers")
|
||||
}
|
||||
|
||||
groupV1.GET("", serverhandlers.HandleV1)
|
||||
v1Handler := api_handlers.V1Handler{
|
||||
Config: cfg,
|
||||
MediaClient: mediaClient,
|
||||
}
|
||||
|
||||
groupV1.POST("/seed", serverhandlers.HandleV1Seed)
|
||||
groupV1.GET("", v1Handler.HandleV1)
|
||||
|
||||
groupV1.GET("/bucket", serverhandlers.HandleV1BucketList)
|
||||
groupV1.POST("/seed", v1Handler.HandleV1Seed)
|
||||
|
||||
groupV1.GET("/bucket/:bucket", serverhandlers.HandleV1BucketRead)
|
||||
groupV1.GET("/bucket", v1Handler.HandleV1BucketList)
|
||||
|
||||
groupV1.POST("/bucket/:bucket", serverhandlers.HandleV1DocumentCreate)
|
||||
groupV1.GET("/bucket/:bucket", v1Handler.HandleV1BucketRead)
|
||||
|
||||
groupV1.GET("/bucket/:bucket/:document", serverhandlers.HandleV1DocumentRead)
|
||||
groupV1.POST("/bucket/:bucket", v1Handler.HandleV1DocumentCreate)
|
||||
|
||||
groupV1.PUT("/bucket/:bucket/:document", serverhandlers.HandleV1DocumentUpdate)
|
||||
groupV1.GET("/bucket/:bucket/:document", v1Handler.HandleV1DocumentRead)
|
||||
|
||||
groupV1.DELETE("/bucket/:bucket/:document", serverhandlers.HandleV1DocumentDelete)
|
||||
groupV1.PUT("/bucket/:bucket/:document", v1Handler.HandleV1DocumentUpdate)
|
||||
|
||||
groupV1.DELETE("/bucket/:bucket/:document", v1Handler.HandleV1DocumentDelete)
|
||||
|
||||
// HTML Routes
|
||||
apiClient, err := api.NewApiClientFromViper()
|
||||
if err != nil {
|
||||
log.Fatal("Error during NewMediaClientFromViper for API handlers")
|
||||
}
|
||||
|
||||
e.GET("/", handleIndex)
|
||||
webHandler := web_handlers.WebHandler{
|
||||
ApiClient: apiClient,
|
||||
}
|
||||
|
||||
//e.GET("/a-propos", handleAPropos)
|
||||
e.GET("/", web_handlers.HandleIndex)
|
||||
|
||||
//e.GET("/actualite", handleActualite)
|
||||
//e.GET("/a-propos", web_handlers.HandleAPropos)
|
||||
|
||||
//e.GET("/actualite/:article", handleActualiteArticle)
|
||||
//e.GET("/actualite", web_handlers.HandleActualite)
|
||||
|
||||
e.GET("/vie-etudiante", handleVieEtudiante)
|
||||
//e.GET("/actualite/:article", web_handlers.HandleActualiteArticle)
|
||||
|
||||
e.GET("/vie-etudiante/:organisme", handleVieEtudianteOrganisme)
|
||||
e.GET("/vie-etudiante", web_handlers.HandleVieEtudiante)
|
||||
|
||||
e.GET("/documentation", handleDocumentation)
|
||||
e.GET("/vie-etudiante/:organisme", web_handlers.HandleVieEtudianteOrganisme)
|
||||
|
||||
e.GET("/formulaires", handleFormulaires)
|
||||
e.GET("/documentation", webHandler.HandleDocumentation)
|
||||
|
||||
e.GET("/formulaires", web_handlers.HandleFormulaires)
|
||||
|
||||
// Public Routes
|
||||
|
||||
e.GET("/public/documentation/:bucket/:document", handlePublicDocumentation)
|
||||
e.GET("/public/documentation/:bucket/:document", webHandler.HandlePublicDocumentation)
|
||||
|
||||
// Admin Routes
|
||||
|
||||
groupAdmin.GET("", handleAdmin)
|
||||
groupAdmin.GET("", web_handlers.HandleAdmin)
|
||||
|
||||
groupAdmin.GET("/documents/upload", handleAdminDocumentsUpload)
|
||||
groupAdmin.GET("/documents/upload", webHandler.HandleAdminDocumentsUpload)
|
||||
|
||||
groupAdmin.POST("/documents/upload", handleAdminDocumentsUploadPOST)
|
||||
groupAdmin.POST("/documents/upload", webHandler.HandleAdminDocumentsUploadPOST)
|
||||
|
||||
e.Logger.Fatal(e.Start(
|
||||
fmt.Sprintf(":%d", cfg.Server.Port)))
|
||||
|
@ -253,248 +270,3 @@ 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)
|
||||
}
|
||||
|
|
7
models/models.go
Normal file
7
models/models.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package models
|
||||
|
||||
type Bucket struct {
|
||||
Name string
|
||||
DisplayName string
|
||||
Documents []string
|
||||
}
|
92
models/responses.go
Normal file
92
models/responses.go
Normal file
|
@ -0,0 +1,92 @@
|
|||
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
|
||||
}
|
||||
}
|
43
public/css/snackbar.css
Normal file
43
public/css/snackbar.css
Normal file
|
@ -0,0 +1,43 @@
|
|||
.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;
|
||||
}
|
10
public/icones/fermer.svg
Normal file
10
public/icones/fermer.svg
Normal file
|
@ -0,0 +1,10 @@
|
|||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1412_1060)">
|
||||
<path d="M256 16C123.45 16 16 123.45 16 256C16 388.55 123.45 496 256 496C388.55 496 496 388.55 496 256C496 123.45 388.55 16 256 16ZM256 76C355.41 76 436 156.59 436 256C436 355.41 355.41 436 256 436C156.59 436 76 355.41 76 256C76 156.59 156.59 76 256 76ZM175.375 136C174.405 135.995 173.369 136.112 172.312 136.313V136.281C154.015 139.717 127.048 171.024 138.937 182.907L212.094 256.032L138.938 329.158C124.308 343.783 168.213 387.692 182.844 373.064L256 299.906L329.156 373.062C343.786 387.69 387.693 343.782 373.062 329.156L299.906 256.031L373.062 182.907C387.692 168.282 343.787 124.407 329.156 139.032L256 212.157L182.844 139.032C180.784 136.986 178.284 136.017 175.374 136.002L175.375 136Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1412_1060">
|
||||
<rect width="512" height="512" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 965 B |
|
@ -1,381 +0,0 @@
|
|||
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",
|
||||
})
|
||||
}
|
|
@ -12,21 +12,28 @@
|
|||
<div class="wrapper documentationWrapper">
|
||||
<h1 class="heading1">Documentation</h1>
|
||||
<p>
|
||||
{{ range . }}
|
||||
{{ $bucket_name := .Name }}
|
||||
{{ $bucket_display_name := .DisplayName }}
|
||||
<details class="documentationCategorie">
|
||||
<summary class="documentationDescription">{{ $bucket_display_name }}</summary>
|
||||
|
||||
<ul class="documentationListe">
|
||||
{{ range .Documents }}
|
||||
<a class ="documentationLien" href="/public/documentation/{{ $bucket_name }}/{{ . }}"><li class="documentationDocument">{{ . }}</li></a>
|
||||
{{ end}}
|
||||
</ul>
|
||||
</details>
|
||||
{{ 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 }}
|
||||
<details class="documentationCategorie">
|
||||
<summary class="documentationDescription">{{ $bucket_display_name }}</summary>
|
||||
<ul class="documentationListe">
|
||||
{{ range .Documents }}
|
||||
<a class ="documentationLien" href="/public/documentation/{{ $bucket_name }}/{{ . }}"><li class="documentationDocument">{{ . }}</li></a>
|
||||
{{ end}}
|
||||
</ul>
|
||||
</details>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</p>
|
||||
<p>
|
||||
{{ .Message }}
|
||||
</p>
|
||||
</div>
|
||||
{{ template "snackbar-html" }}
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
|
|
|
@ -12,7 +12,29 @@
|
|||
<div class="wrapper indexWrapper">
|
||||
<h1 class="heading1">AGECEM</h1>
|
||||
<h2 class="heading2">Association Générale Étudiante du Cégep Édouard-Montpetit</h2>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<br>
|
||||
<h2 class="heading2">Contact</h2>
|
||||
<h3>Courriel</h3>
|
||||
permanence@agecem.org
|
||||
<h3>Téléphone</h3>
|
||||
(450) 679-7375
|
||||
<h3>Addresse de coordination</h3>
|
||||
945 Chemin de Chambly, Longueuil, QC J4H 3M6
|
||||
<h3>Local</h3>
|
||||
B-31
|
||||
<h3>Réseaux sociaux</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.facebook.com/asso.agecem">Facebook</a></li>
|
||||
<li><a href="https://www.instagram.com/agecem_officiel"/>Instagram</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{{ template "snackbar-html" }}
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
|
|
15
templates/html/snackbar.gohtml
Normal file
15
templates/html/snackbar.gohtml
Normal file
|
@ -0,0 +1,15 @@
|
|||
{{ define "snackbar-html" }}
|
||||
<link rel="stylesheet" href="/public/css/snackbar.css">
|
||||
<script>
|
||||
function closeSnackbar() {
|
||||
var snackbar = document.querySelector(".snackbar");
|
||||
snackbar.classList.add('snackbar-is-closed');
|
||||
}
|
||||
</script>
|
||||
<div class="snackbar">
|
||||
<div class="wrapper snackbarWrapper">
|
||||
<span class="snackbarTexte">Ce site web est présentement en construction.</span>
|
||||
<img src="/public/icones/fermer.svg" class="snackbarFermer" onclick="closeSnackbar()"></img>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
|
@ -10,7 +10,147 @@
|
|||
{{ template "header-html" }}
|
||||
<div class="wrapper vieEtudianteWrapper">
|
||||
<h1 class="heading1">Vie étudiante</h1>
|
||||
<h3>Organismes thématiques</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Nom</td>
|
||||
<td>Local</td>
|
||||
<td>Poste téléphonique</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AME</td>
|
||||
<td>C-060</td>
|
||||
<td>7919</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>BEAM</td>
|
||||
<td>F-024a</td>
|
||||
<td>5930</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CIC</td>
|
||||
<td>F-027c</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Club Aventurier</td>
|
||||
<td>F-011b</td>
|
||||
<td>2730</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Équipe Santé</td>
|
||||
<td>F-011</td>
|
||||
<td>2361</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Montpetit Donjon</td>
|
||||
<td>C-067</td>
|
||||
<td>2299</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MAEL</td>
|
||||
<td>F-027b</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OGRE</td>
|
||||
<td>F-011c</td>
|
||||
<td>5647</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Radio</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ORGASME</td>
|
||||
<td>F-027d</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SOI</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MotDit</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
<h3>Associations de programme</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Nom</td>
|
||||
<td>Local</td>
|
||||
<td>Poste téléphonique</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ADEPT</td>
|
||||
<td>F-045</td>
|
||||
<td>2286</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ASI</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ATIM</td>
|
||||
<td>F-041</td>
|
||||
<td>2652</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AEALC</td>
|
||||
<td>A-125r</td>
|
||||
<td>2873</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PAPI</td>
|
||||
<td>F-023</td>
|
||||
<td>2795</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TEE</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TGE</td>
|
||||
<td>C-063</td>
|
||||
<td>2638</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
<h3>Comités</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Nom</td>
|
||||
<td>Local</td>
|
||||
<td>Poste téléphonique</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CAP</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ESPACE</td>
|
||||
<td>F-011d</td>
|
||||
<td>2418</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CFEM</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ASEG</td>
|
||||
<td>B-06</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Comité Mob</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>EUMC-CEM</td>
|
||||
<td>C-054</td>
|
||||
<td>2356</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CÉSI</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Friperie</td>
|
||||
<td>F-027a</td>
|
||||
<td>2248</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{{ template "snackbar-html" }}
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
|
|
204
web_handlers/web_handlers.go
Normal file
204
web_handlers/web_handlers.go
Normal file
|
@ -0,0 +1,204 @@
|
|||
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)
|
||||
}
|
Loading…
Reference in a new issue