Si on suit les considérations du domain-driven design, le fait qu'on réfère toustes à cette partie du code comme `le frontend` me pousse à renommer le package pour mieux réflèter notre utilisation. Ça devrait rendre plus évident où les fichiers sources du frontend sont situés.
973 lines
27 KiB
Go
973 lines
27 KiB
Go
package agecemorg
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"sort"
|
|
|
|
"codeberg.org/vlbeaudoin/pave/v2"
|
|
"codeberg.org/vlbeaudoin/voki/v3"
|
|
"git.agecem.com/agecem/agecem-org/v3/frontend"
|
|
"github.com/labstack/echo/v4"
|
|
"github.com/minio/minio-go/v7"
|
|
)
|
|
|
|
type V1Handler struct {
|
|
Config Config
|
|
MediaClient *MediaClient
|
|
Pave *pave.Pave
|
|
}
|
|
|
|
// API Handlers
|
|
|
|
// ListRoutes affiche les routes accessibles.
|
|
// Les routes sont triées selon .Path, pour les rendre plus facilement navigables.
|
|
func (h *V1Handler) ListRoutes(c echo.Context) error {
|
|
routes := c.Echo().Routes()
|
|
sort.Slice(routes, func(i, j int) bool { return routes[i].Path < routes[j].Path })
|
|
return c.JSON(http.StatusOK, routes)
|
|
}
|
|
|
|
// ListBuckets affiche les buckets permis par server.documents.buckets, qui existent.
|
|
func (h *V1Handler) ListBuckets(c echo.Context) error {
|
|
var request ListBucketsRequest
|
|
var response ListBucketsResponse
|
|
|
|
if !request.Complete() {
|
|
response.Message = "Incomplete ListBuckets request received"
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
var buckets = make(map[string]string)
|
|
|
|
for bucket_name, bucket_display_name := range h.Config.Server.Documents.Buckets {
|
|
exists, err := h.MediaClient.MinioClient.BucketExists(context.Background(), bucket_name)
|
|
if err != nil {
|
|
response.Message = "Error during minio#BucketExists"
|
|
response.SetStatusCode(http.StatusInternalServerError)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
if exists {
|
|
buckets[bucket_name] = bucket_display_name
|
|
}
|
|
}
|
|
|
|
response.SetStatusCode(http.StatusOK)
|
|
response.Message = "Buckets list successful"
|
|
response.Data.Buckets = buckets
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
func (h *V1Handler) ReadBucket(c echo.Context) error {
|
|
var request ReadBucketRequest
|
|
var response ReadBucketResponse
|
|
|
|
request.Params.Bucket = c.Param("bucket")
|
|
|
|
if !request.Complete() {
|
|
response.Message = "Incomplete ReadBucket request received"
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
allowed := false
|
|
for bucket_allowed := range h.Config.Server.Documents.Buckets {
|
|
if request.Params.Bucket == bucket_allowed {
|
|
allowed = true
|
|
}
|
|
}
|
|
|
|
if !allowed {
|
|
response := voki.ResponseNotFound{}
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
defer cancel()
|
|
|
|
exists, err := h.MediaClient.MinioClient.BucketExists(ctx, request.Params.Bucket)
|
|
if err != nil {
|
|
response.Message = "Error during minio#BucketExists"
|
|
response.SetStatusCode(http.StatusInternalServerError)
|
|
response.Error = err.Error()
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
if !exists {
|
|
response := voki.ResponseNotFound{}
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
objectCh := h.MediaClient.MinioClient.ListObjects(ctx, request.Params.Bucket, minio.ListObjectsOptions{})
|
|
for object := range objectCh {
|
|
if object.Err != nil {
|
|
response.Message = "Error during minio#ListObjects"
|
|
response.SetStatusCode(http.StatusInternalServerError)
|
|
//TODO make sure this is safe
|
|
//response.Error = object.Err.Error()
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
response.Data.Keys = append(response.Data.Keys, object.Key)
|
|
}
|
|
|
|
response.Message = "V1BucketRead ok"
|
|
response.SetStatusCode(http.StatusOK)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
/*
|
|
CreateDocuments permet d'ajouter un object dans un bucket, par multipart/form-data
|
|
|
|
Example:
|
|
|
|
Téléverser plusieurs fichiers à cette route avec `curl`:
|
|
|
|
curl <endpoint> -F 'documents=@example.pdf' -F 'documents=@example.md;type=text/markdown'
|
|
*/
|
|
func (h *V1Handler) CreateDocuments(c echo.Context) (err error) {
|
|
var request CreateDocumentsRequest
|
|
var response CreateDocumentsResponse
|
|
|
|
request.Params.Bucket = c.Param("bucket")
|
|
|
|
var allowed bool
|
|
for allowedBucket := range h.Config.Server.Documents.Buckets {
|
|
if request.Params.Bucket == allowedBucket {
|
|
allowed = true
|
|
}
|
|
}
|
|
|
|
if !allowed {
|
|
response := voki.ResponseNotFound{}
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
form, err := c.MultipartForm()
|
|
if err != nil {
|
|
response.Message = fmt.Sprintf("Téléversement invalide: %s", err)
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
if form == nil {
|
|
response.Message = "MultipartForm pointe vers une addresse mémoire nil"
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
if len(form.File) == 0 {
|
|
response.Message = "Veuillez sélectionner au moins 1 document à téléverser"
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
for inputName, inputFileHeaders := range form.File {
|
|
if inputName == "documents" {
|
|
request.Data.Documents = inputFileHeaders
|
|
}
|
|
}
|
|
|
|
if request.Data.Documents == nil {
|
|
response.Message = "Impossible d'obtenir les documents depuis le formulaire"
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
if !request.Complete() {
|
|
response.Message = "Requête CreateDocuments incomplète reçue"
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
var code int
|
|
code, response.Message = h.MediaClient.UploadFormFiles(request.Params.Bucket, request.Data.Documents)
|
|
response.SetStatusCode(code)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
// CreateDocument permet d'ajouter un object dans un bucket, par multipart/form-data
|
|
func (h *V1Handler) CreateDocument(c echo.Context) (err error) {
|
|
var request CreateDocumentRequest
|
|
var response CreateDocumentResponse
|
|
|
|
request.Params.Bucket = c.Param("bucket")
|
|
|
|
request.Data.Document, err = c.FormFile("document")
|
|
if err != nil {
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
response.Message = "Error during CreateDocument's echo#Context.FormFile"
|
|
response.Error = err.Error()
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
allowed := false
|
|
for bucket_allowed := range h.Config.Server.Documents.Buckets {
|
|
if request.Params.Bucket == bucket_allowed {
|
|
allowed = true
|
|
}
|
|
}
|
|
|
|
if !allowed {
|
|
response := voki.ResponseNotFound{}
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
if !request.Complete() {
|
|
response.Message = "Incomplete CreateDocument request received"
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
defer cancel()
|
|
|
|
src, err := request.Data.Document.Open()
|
|
if err != nil {
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
response.Message = "Error during form_file.Open()"
|
|
response.Error = err.Error()
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
defer src.Close()
|
|
|
|
info, err := h.MediaClient.MinioClient.PutObject(ctx, request.Params.Bucket, request.Data.Document.Filename, src, request.Data.Document.Size, minio.PutObjectOptions{
|
|
ContentType: request.Data.Document.Header.Get("Content-Type"),
|
|
})
|
|
if err != nil {
|
|
response.SetStatusCode(http.StatusInternalServerError)
|
|
response.Message = "Error during minio#PutObject"
|
|
//response.Error = err.Error()
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
response.SetStatusCode(http.StatusOK)
|
|
response.Message = "ok"
|
|
response.Data.Bucket = info.Bucket
|
|
response.Data.Key = info.Key
|
|
response.Data.Size = info.Size
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
// ReadDocument permet de lire le contenu d'un fichier et protentiellement de le télécharger
|
|
func (h *V1Handler) ReadDocument(c echo.Context) error {
|
|
bucket := c.Param("bucket")
|
|
document := c.Param("document")
|
|
|
|
allowed := false
|
|
for bucket_allowed := range h.Config.Server.Documents.Buckets {
|
|
if bucket == bucket_allowed {
|
|
allowed = true
|
|
}
|
|
}
|
|
|
|
if !allowed {
|
|
response := voki.ResponseNotFound{}
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
defer cancel()
|
|
|
|
bucket_exists, err := h.MediaClient.MinioClient.BucketExists(ctx, bucket)
|
|
if err != nil {
|
|
return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists")
|
|
}
|
|
|
|
if !bucket_exists {
|
|
response := voki.ResponseNotFound{}
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
document_info, err := h.MediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{})
|
|
|
|
if err != nil {
|
|
if err.Error() == "The specified key does not exist." {
|
|
|
|
response := voki.ResponseNotFound{}
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
return c.JSON(http.StatusInternalServerError, map[string]interface{}{
|
|
"message": "Error during minio#StatObject",
|
|
})
|
|
}
|
|
|
|
_ = document_info
|
|
|
|
document_object, err := h.MediaClient.MinioClient.GetObject(ctx, bucket, document, minio.GetObjectOptions{})
|
|
if err != nil {
|
|
return c.JSON(http.StatusInternalServerError, map[string]string{
|
|
"message": "Error during minio#GetObject",
|
|
})
|
|
}
|
|
|
|
defer document_object.Close()
|
|
|
|
return c.Stream(http.StatusOK, document_info.ContentType, document_object)
|
|
}
|
|
|
|
// DeleteDocument permet de supprimer un object
|
|
func (h *V1Handler) DeleteDocument(c echo.Context) error {
|
|
var request DeleteDocumentRequest
|
|
var response DeleteDocumentResponse
|
|
|
|
request.Params.Bucket = c.Param("bucket")
|
|
request.Params.Document = c.Param("document")
|
|
|
|
allowed := false
|
|
for bucket_allowed := range h.Config.Server.Documents.Buckets {
|
|
if request.Params.Bucket == bucket_allowed {
|
|
allowed = true
|
|
}
|
|
}
|
|
|
|
if !allowed {
|
|
response := voki.ResponseNotFound{}
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
if !request.Complete() {
|
|
response.Message = "Incomplete DeleteDocument request received"
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
defer cancel()
|
|
|
|
bucket_exists, err := h.MediaClient.MinioClient.BucketExists(ctx, request.Params.Bucket)
|
|
if err != nil {
|
|
return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists")
|
|
}
|
|
|
|
if !bucket_exists {
|
|
response := voki.ResponseNotFound{}
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
document_info, err := h.MediaClient.MinioClient.StatObject(ctx, request.Params.Bucket, request.Params.Document, minio.StatObjectOptions{})
|
|
if err != nil {
|
|
if err.Error() == "The specified key does not exist." {
|
|
|
|
response := voki.ResponseNotFound{}
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
//response.Error = err.Error()
|
|
response.Message = "Error during minio#StatObject"
|
|
response.SetStatusCode(http.StatusInternalServerError)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
//TODO Add error validation
|
|
_ = document_info
|
|
|
|
err = h.MediaClient.MinioClient.RemoveObject(ctx, request.Params.Bucket, request.Params.Document, minio.RemoveObjectOptions{})
|
|
if err != nil {
|
|
//response.Error = err.Error()
|
|
response.Message = "Error during minio#RemoveObject"
|
|
response.SetStatusCode(http.StatusInternalServerError)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
response.Message = "Document deleted"
|
|
response.SetStatusCode(http.StatusOK)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
// UpdateDocumentKey
|
|
func (h *V1Handler) UpdateDocumentKey(c echo.Context) (err error) {
|
|
var request UpdateDocumentKeyRequest
|
|
var response UpdateDocumentKeyResponse
|
|
|
|
request.Params.Bucket = c.Param("bucket")
|
|
request.Params.Document = c.Param("document")
|
|
|
|
var newKey string
|
|
err = c.Bind(&newKey)
|
|
if err != nil {
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
response.Message = err.Error()
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
request.Data.NewKey = newKey
|
|
|
|
if !request.Complete() {
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
response.Message = "Incomplete UpdateDocumentKey request received"
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
var allowed bool
|
|
for bucketAllowed := range h.Config.Server.Documents.Buckets {
|
|
if request.Params.Bucket == bucketAllowed {
|
|
allowed = true
|
|
}
|
|
}
|
|
|
|
if !allowed {
|
|
response := voki.ResponseNotFound{}
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
bucketExists, err := h.MediaClient.MinioClient.BucketExists(ctx, request.Params.Bucket)
|
|
if err != nil {
|
|
return c.JSON(http.StatusInternalServerError, "Could not validate bucket exists")
|
|
}
|
|
|
|
if !bucketExists {
|
|
response := voki.ResponseNotFound{}
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
// Check source object exists
|
|
|
|
if _, err := h.MediaClient.MinioClient.StatObject(ctx, request.Params.Bucket, request.Params.Document, minio.StatObjectOptions{}); err != nil {
|
|
if err.Error() == "The specified key does not exist." {
|
|
|
|
response := voki.ResponseNotFound{}
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
response.Message = fmt.Sprintf("Could not obtain information on %s/%s", request.Params.Bucket, request.Params.Document)
|
|
response.SetStatusCode(http.StatusInternalServerError)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
// Copy object to newKey
|
|
|
|
if _, err := h.MediaClient.MinioClient.CopyObject(ctx,
|
|
minio.CopyDestOptions{Bucket: request.Params.Bucket, Object: request.Data.NewKey},
|
|
minio.CopySrcOptions{Bucket: request.Params.Bucket, Object: request.Params.Document},
|
|
); err != nil {
|
|
response.SetStatusCode(http.StatusInternalServerError)
|
|
response.Message = "Impossible de copier un document pour le renommer"
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
// Verify copy was successful
|
|
if _, err := h.MediaClient.MinioClient.StatObject(ctx,
|
|
request.Params.Bucket, request.Data.NewKey, minio.GetObjectOptions{}); err != nil {
|
|
response.SetStatusCode(http.StatusInternalServerError)
|
|
response.Message = "Copie de document ne semble pas avoir fonctionnée"
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
// Delete old file
|
|
if err := h.MediaClient.MinioClient.RemoveObject(ctx,
|
|
request.Params.Bucket, request.Params.Document, minio.RemoveObjectOptions{}); err != nil {
|
|
response.SetStatusCode(http.StatusInternalServerError)
|
|
response.Message = "Erreur pendant tentative de supprimer document source"
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
//TODO cleanup
|
|
|
|
// Return result
|
|
response.SetStatusCode(http.StatusOK)
|
|
response.Message = "Document renommé avec succès"
|
|
response.Data.Bucket = request.Params.Bucket
|
|
response.Data.Key = request.Data.NewKey
|
|
return c.JSON(response.StatusCode(), response)
|
|
|
|
}
|
|
|
|
// ExecuteSeed créé des buckets dans minio selon la liste de buckets dans server.documents.buckets
|
|
// Les buckets sont créés avec paramètres par défaut, et sont ensuite visible dans /v1/bucket.
|
|
func (h *V1Handler) ExecuteSeed(c echo.Context) error {
|
|
//var request ExecuteSeedRequest
|
|
var response ExecuteSeedResponse
|
|
|
|
new_buckets, err := h.MediaClient.Seed()
|
|
response.Data.Buckets = new_buckets
|
|
if err != nil {
|
|
response.SetStatusCode(http.StatusInternalServerError)
|
|
response.Message = "Error during mediaClient.Seed()"
|
|
response.Error = err.Error()
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
if len(new_buckets) == 0 {
|
|
response.Message = "All buckets already exist"
|
|
|
|
} else {
|
|
response.Message = "Buckets successfully created"
|
|
}
|
|
|
|
response.SetStatusCode(http.StatusOK)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
const DescriptionV1SpecGET string = "Afficher le API spec en format pave"
|
|
|
|
func (h *V1Handler) ReadSpec(c echo.Context) error {
|
|
var request ReadSpecRequest
|
|
var response ReadSpecResponse
|
|
|
|
if !request.Complete() {
|
|
response.Message = "Incomplete ReadSpec request received"
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
response.Data.Spec = fmt.Sprintf("# pave spec for agecem-org %s\n", Version())
|
|
|
|
for _, route := range h.Pave.SortedRouteStrings() {
|
|
response.Data.Spec = fmt.Sprintf("%s%s", response.Data.Spec, route)
|
|
}
|
|
|
|
response.Message = "ok"
|
|
response.SetStatusCode(http.StatusOK)
|
|
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
|
|
type WebHandler struct {
|
|
ApiClient *API
|
|
}
|
|
|
|
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 {
|
|
var data struct {
|
|
Data struct {
|
|
Organismes map[string][]struct {
|
|
Nom string
|
|
Local string
|
|
Poste string
|
|
Statut string
|
|
Description string
|
|
}
|
|
}
|
|
Error string
|
|
}
|
|
|
|
if err := json.Unmarshal([]byte(frontend.OrganismesJSON()), &data.Data.Organismes); err != nil {
|
|
data.Error = err.Error()
|
|
log.Println("json error in vie-etudiante:", err)
|
|
}
|
|
|
|
return c.Render(http.StatusOK, "vie-etudiante-html", data)
|
|
}
|
|
|
|
func HandleVieEtudianteOrganisme(c echo.Context) error {
|
|
organisme := c.Param("organisme")
|
|
return c.String(http.StatusOK, fmt.Sprintf("Organisme: %s", organisme))
|
|
}
|
|
|
|
func (h *WebHandler) HandleDocumentation(c echo.Context) error {
|
|
var response HandleDocumentationResponse
|
|
|
|
v1BucketsGET, err := h.ApiClient.ListBuckets()
|
|
if err != nil {
|
|
response.Error = err.Error()
|
|
response.Message = v1BucketsGET.Message
|
|
response.SetStatusCode(v1BucketsGET.StatusCode())
|
|
|
|
return c.Render(response.StatusCode(), "documentation-html", response)
|
|
}
|
|
|
|
//TODO check v1BucketsGET StatusCode and Error
|
|
|
|
for bucket, displayName := range v1BucketsGET.Data.Buckets {
|
|
// TODO move call to dedicated API client method
|
|
var v1BucketReadResponse ReadBucketResponse
|
|
|
|
if err = h.ApiClient.Voki.Unmarshal(http.MethodGet, fmt.Sprintf("/v1/bucket/%s", bucket), nil, true, &v1BucketReadResponse); err != nil {
|
|
response.Error = err.Error()
|
|
response.Message = "Error during json.Unmarshal /v1/bucket/:bucket"
|
|
response.SetStatusCode(http.StatusInternalServerError)
|
|
|
|
return c.Render(http.StatusOK, "documentation-html", response)
|
|
}
|
|
|
|
response.Data.Buckets = append(response.Data.Buckets, Bucket{
|
|
Name: bucket,
|
|
DisplayName: displayName,
|
|
Documents: v1BucketReadResponse.Data.Keys,
|
|
})
|
|
}
|
|
|
|
sort.SliceStable(response.Data.Buckets, func(i, j int) bool { return response.Data.Buckets[i].Name < response.Data.Buckets[j].Name })
|
|
|
|
response.SetStatusCode(http.StatusOK)
|
|
//response.Message = "HandleDocumentation ok"
|
|
|
|
// TODO render .Message
|
|
return c.Render(http.StatusOK, "documentation-html", response)
|
|
//return c.Render(response.StatusCode(), "documentation-html", response.Data.Buckets)
|
|
}
|
|
|
|
func HandleFormulaires(c echo.Context) error {
|
|
return c.Render(http.StatusOK, "formulaires-html", nil)
|
|
}
|
|
|
|
func (h *WebHandler) HandlePublicDocumentation(c echo.Context) error {
|
|
bucket := c.Param("bucket")
|
|
document := c.Param("document")
|
|
|
|
unescaped, err := url.QueryUnescape(fmt.Sprintf("/v1/bucket/%s/%s", bucket, document))
|
|
if err != nil {
|
|
return c.JSON(http.StatusBadRequest, map[string]string{"message": "Bad Request"})
|
|
}
|
|
|
|
response, err := h.ApiClient.Voki.Call(http.MethodGet, unescaped, nil, true)
|
|
if err != nil {
|
|
response := voki.ResponseNotFound{}
|
|
return c.JSON(response.StatusCode(), response)
|
|
}
|
|
defer response.Body.Close()
|
|
|
|
switch response.StatusCode {
|
|
case http.StatusNotFound:
|
|
response := voki.ResponseNotFound{}
|
|
return c.JSON(response.StatusCode(), response)
|
|
case http.StatusInternalServerError:
|
|
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "Internal Server Error"})
|
|
}
|
|
|
|
body, err := io.ReadAll(response.Body)
|
|
if err != nil {
|
|
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "Internal Server Error"})
|
|
}
|
|
|
|
return c.Blob(http.StatusOK, "application/octet-stream", body)
|
|
}
|
|
|
|
func HandleAdmin(c echo.Context) error {
|
|
return c.Render(http.StatusOK, "admin-html", nil)
|
|
}
|
|
|
|
func HandlePageProcesVerbaux() echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
return c.Render(http.StatusOK, "procesverbaux-html", nil)
|
|
}
|
|
}
|
|
|
|
func HandleAdminDocumentsEditDelete(api *API) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
switch c.Request().Method {
|
|
case http.MethodGet:
|
|
var data struct {
|
|
Error string
|
|
Bucket string
|
|
Document string
|
|
}
|
|
|
|
data.Bucket = c.Param("bucket")
|
|
data.Document = c.Param("document")
|
|
|
|
return c.Render(http.StatusOK, "dialog-document-delete", data)
|
|
case http.MethodDelete:
|
|
var data struct {
|
|
Error string
|
|
Bucket string
|
|
Document string
|
|
DeleteDocumentResponse DeleteDocumentResponse
|
|
}
|
|
if api == nil {
|
|
errMsg := "Error: HandleAdminDocumentsEditDelete called with nil *API"
|
|
log.Println(errMsg)
|
|
data.Error = errMsg
|
|
|
|
return c.String(http.StatusOK, data.Error)
|
|
}
|
|
data.Bucket = c.Param("bucket")
|
|
data.Document = c.Param("document")
|
|
|
|
deleteDocumentResponse, err := api.DeleteDocument(data.Bucket, data.Document)
|
|
if err != nil {
|
|
log.Println(err)
|
|
data.Error = err.Error()
|
|
return c.Render(http.StatusOK, "dialog-error", data)
|
|
}
|
|
data.DeleteDocumentResponse = deleteDocumentResponse
|
|
|
|
return c.Render(http.StatusOK, "dialog-document-delete-result", data)
|
|
default:
|
|
return c.String(http.StatusOK, "unsupported method")
|
|
}
|
|
}
|
|
}
|
|
|
|
func HandleAdminDocumentsEditKey(api *API) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
switch c.Request().Method {
|
|
case http.MethodGet:
|
|
var data struct {
|
|
Error string
|
|
Bucket string
|
|
Document string
|
|
}
|
|
|
|
data.Bucket = c.Param("bucket")
|
|
data.Document = c.Param("document")
|
|
|
|
return c.Render(http.StatusOK, "dialog-document-rename", data)
|
|
case http.MethodPut:
|
|
var data struct {
|
|
Error string
|
|
Bucket string
|
|
Document string
|
|
UpdateDocumentKeyResponse UpdateDocumentKeyResponse
|
|
}
|
|
if api == nil {
|
|
errMsg := "Error: HandleAdminDocumentsEdit called with nil *API"
|
|
log.Println(errMsg)
|
|
data.Error = errMsg
|
|
|
|
return c.String(http.StatusOK, data.Error)
|
|
}
|
|
|
|
data.Bucket = c.Param("bucket")
|
|
data.Document = c.Param("document")
|
|
|
|
newKey := c.FormValue("rename-document-form-newkey")
|
|
|
|
updateDocumentKeyResponse, err := api.UpdateDocumentKey(data.Bucket, data.Document, newKey)
|
|
if err != nil {
|
|
log.Println(err)
|
|
data.Error = err.Error()
|
|
return c.Render(http.StatusOK, "dialog-error", data)
|
|
}
|
|
data.UpdateDocumentKeyResponse = updateDocumentKeyResponse
|
|
|
|
return c.Render(http.StatusOK, "dialog-document-rename-result", data)
|
|
default:
|
|
return c.String(http.StatusOK, "unsupported method")
|
|
}
|
|
}
|
|
}
|
|
|
|
func HandleNothing() echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
return c.String(http.StatusOK, "")
|
|
}
|
|
}
|
|
|
|
func HandleAdminDocumentsEdit(api *API) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
type DataBucket struct {
|
|
ID string
|
|
DisplayName string
|
|
DocumentKeys []string
|
|
}
|
|
var data struct {
|
|
Error string
|
|
Buckets []DataBucket
|
|
}
|
|
|
|
if api == nil {
|
|
errMsg := "Error: HandleAdminDocumentsEdit called with nil *API"
|
|
log.Println(errMsg)
|
|
data.Error = errMsg
|
|
return c.Render(http.StatusOK, "admin-edit-html", data)
|
|
}
|
|
|
|
listBucketsResponse, err := api.ListBuckets()
|
|
if err != nil {
|
|
log.Println(err)
|
|
data.Error = err.Error()
|
|
return c.Render(http.StatusOK, "admin-edit-html", data)
|
|
}
|
|
for name, displayName := range listBucketsResponse.Data.Buckets {
|
|
readBucketResponse, err := api.ReadBucket(name)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
dataBucket := DataBucket{
|
|
ID: name,
|
|
DisplayName: displayName,
|
|
DocumentKeys: readBucketResponse.Data.Keys,
|
|
}
|
|
|
|
data.Buckets = append(data.Buckets, dataBucket)
|
|
}
|
|
|
|
return c.Render(http.StatusOK, "admin-edit-html", data)
|
|
}
|
|
}
|
|
|
|
func (h *WebHandler) HandleAdminDocumentsUpload(c echo.Context) error {
|
|
var response HandleAdminDocumentsUploadResponse
|
|
|
|
v1BucketsGET, err := h.ApiClient.ListBuckets()
|
|
if err != nil {
|
|
response.SetStatusCode(v1BucketsGET.StatusCode())
|
|
response.Error = err.Error()
|
|
response.Message = v1BucketsGET.Message
|
|
|
|
return c.Render(response.StatusCode(), "admin-upload-html", nil)
|
|
}
|
|
|
|
for bucketName, displayName := range v1BucketsGET.Data.Buckets {
|
|
response.Data.Buckets = append(response.Data.Buckets, Bucket{
|
|
Name: bucketName,
|
|
DisplayName: displayName,
|
|
})
|
|
}
|
|
response.SetStatusCode(http.StatusOK)
|
|
|
|
return c.Render(response.StatusCode(), "admin-upload-html", response)
|
|
}
|
|
|
|
func (h *WebHandler) HandleAdminDocumentsUploadPOST(c echo.Context) error {
|
|
var request CreateDocumentsRequest
|
|
var response HandleAdminDocumentsUploadResponse
|
|
|
|
v1BucketsGET, err := h.ApiClient.ListBuckets()
|
|
if err != nil {
|
|
response.SetStatusCode(v1BucketsGET.StatusCode())
|
|
response.Message = v1BucketsGET.Message
|
|
response.Error = err.Error()
|
|
|
|
return c.Render(response.StatusCode(), "admin-upload-html", response)
|
|
}
|
|
|
|
for bucketName, displayName := range v1BucketsGET.Data.Buckets {
|
|
response.Data.Buckets = append(response.Data.Buckets, Bucket{
|
|
Name: bucketName,
|
|
DisplayName: displayName,
|
|
})
|
|
}
|
|
|
|
request.Params.Bucket = c.FormValue("bucket")
|
|
|
|
form, err := c.MultipartForm()
|
|
if err != nil {
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
response.Message = "Impossible de téléverser"
|
|
response.Error = err.Error()
|
|
|
|
return c.Render(response.StatusCode(), "admin-upload-html", response)
|
|
}
|
|
if form == nil {
|
|
response.SetStatusCode(http.StatusInternalServerError)
|
|
response.Message = "Formulaire pointe vers une addresse mémoire nulle"
|
|
response.Error = "Formulaire pointe vers une addresse mémoire nulle"
|
|
|
|
return c.Render(response.StatusCode(), "admin-upload-html", response)
|
|
}
|
|
|
|
if len(form.File) == 0 {
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
response.Message = "Veuillez sélectionner au moins 1 fichier à téléverser"
|
|
response.Error = "Input 'documents' ne contient aucun fichier"
|
|
|
|
return c.Render(response.StatusCode(), "admin-upload-html", response)
|
|
}
|
|
|
|
for inputName, inputFileHeaders := range form.File {
|
|
if inputName == "documents" {
|
|
request.Data.Documents = inputFileHeaders
|
|
break
|
|
}
|
|
}
|
|
|
|
if request.Data.Documents == nil {
|
|
response.SetStatusCode(http.StatusBadRequest)
|
|
response.Message = "Impossible d'obtenir les documents depuis le formulaire"
|
|
response.Error = "Impossible d'obtenir les documents depuis le formulaire"
|
|
|
|
return c.Render(response.StatusCode(), "admin-upload-html", response)
|
|
}
|
|
|
|
uploadDocumentsResponse, err := h.ApiClient.UploadDocuments(request.Params.Bucket, request.Data.Documents...)
|
|
if err != nil {
|
|
//TODO figure out pourquoi `err` n'est jamais `nil`
|
|
response.SetStatusCode(uploadDocumentsResponse.StatusCode())
|
|
response.Message = uploadDocumentsResponse.Message
|
|
response.Error = fmt.Sprintf("%s. Détails: %s", err.Error(), uploadDocumentsResponse.Error)
|
|
/*
|
|
response.SetStatusCode(http.StatusInternalServerError)
|
|
response.Message = fmt.Sprintf("api.(*API).UploadDocuments: %s", err)
|
|
response.Error = err.Error()
|
|
*/
|
|
|
|
return c.Render(response.StatusCode(), "admin-upload-html", response)
|
|
}
|
|
|
|
//TODO figure out pourquoi on se rend jamais ici
|
|
|
|
// Format response
|
|
var info, status string
|
|
|
|
for i, document := range uploadDocumentsResponse.Data.Documents {
|
|
info = fmt.Sprintf("%s[%d] /public/documentation/%s/%s (%dk) ok\n",
|
|
info, i, uploadDocumentsResponse.Data.Bucket, document.Key, document.Size)
|
|
}
|
|
|
|
status = uploadDocumentsResponse.Message
|
|
if errMsg := uploadDocumentsResponse.Error; errMsg != "" {
|
|
status = fmt.Sprintf("%s. Erreur: %s", status, errMsg)
|
|
}
|
|
|
|
response.SetStatusCode(http.StatusOK)
|
|
response.Message = fmt.Sprintf("%s \n %s", status, info)
|
|
|
|
return c.Render(response.StatusCode(), "admin-upload-html", response)
|
|
}
|