Victor Lacasse-Beaudoin
7bf489315e
Exposes the API spec in pave format, which intends to show information about all API routes. Also pave V1SeedPOST and V1SpecGET
315 lines
8.4 KiB
Go
315 lines
8.4 KiB
Go
package apihandler
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"sort"
|
|
|
|
"codeberg.org/vlbeaudoin/pave"
|
|
"git.agecem.com/agecem/agecem-org/apiresponse"
|
|
"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"
|
|
)
|
|
|
|
type V1Handler struct {
|
|
Config config.Config
|
|
MediaClient *media.MediaClient
|
|
Pave *pave.Pave
|
|
}
|
|
|
|
// 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 apiresponse.V1SeedPOST
|
|
|
|
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 apiresponse.V1BucketList
|
|
|
|
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 apiresponse.V1BucketRead
|
|
|
|
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(apiresponse.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(apiresponse.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 apiresponse.V1DocumentCreate
|
|
|
|
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(apiresponse.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(apiresponse.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(apiresponse.NotFoundResponse())
|
|
}
|
|
|
|
document_info, err := h.MediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{})
|
|
|
|
if err != nil {
|
|
if err.Error() == "The specified key does not exist." {
|
|
|
|
return c.JSON(apiresponse.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(apiresponse.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(apiresponse.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(apiresponse.NotFoundResponse())
|
|
}
|
|
|
|
document_info, err := h.MediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{})
|
|
if err != nil {
|
|
if err.Error() == "The specified key does not exist." {
|
|
|
|
return c.JSON(apiresponse.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",
|
|
})
|
|
}
|