184 lines
5.7 KiB
Go
184 lines
5.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"mime"
|
|
"mime/multipart"
|
|
"net/http"
|
|
|
|
"github.com/minio/minio-go/v7"
|
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
func NewMediaClient(endpoint, accessKeyId, secretAccessKey string, useSSL bool) (*MediaClient, error) {
|
|
if accessKeyId == "" {
|
|
return nil, errors.New("accessKeyId was found empty, but cannot be")
|
|
}
|
|
|
|
if secretAccessKey == "" {
|
|
return nil, errors.New("secretAccessKey was found empty, but cannot be")
|
|
}
|
|
|
|
var mediaClient MediaClient
|
|
minioClient, err := minio.New(endpoint, &minio.Options{
|
|
Creds: credentials.NewStaticV4(accessKeyId, secretAccessKey, ""),
|
|
Secure: useSSL,
|
|
})
|
|
if err != nil {
|
|
return &mediaClient, err
|
|
}
|
|
|
|
mediaClient.MinioClient = *minioClient
|
|
return &mediaClient, nil
|
|
}
|
|
|
|
func NewMediaClientFromViper() (*MediaClient, error) {
|
|
var cfg Config
|
|
if err := viper.Unmarshal(&cfg); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
mediaClient, err := NewMediaClient(cfg.Server.Documents.Endpoint, cfg.Server.Documents.AccessKeyId, cfg.Server.Documents.SecretAccessKey, cfg.Server.Documents.UseSSL)
|
|
if err != nil {
|
|
return mediaClient, err
|
|
}
|
|
|
|
return mediaClient, nil
|
|
}
|
|
|
|
type MediaClient struct {
|
|
MinioClient minio.Client
|
|
}
|
|
|
|
func (m *MediaClient) Seed() ([]string, error) {
|
|
var cfg Config
|
|
if err := viper.Unmarshal(&cfg); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var new_buckets []string
|
|
|
|
for bucket := range cfg.Server.Documents.Buckets {
|
|
exists, err := m.MinioClient.BucketExists(context.Background(), bucket)
|
|
if err != nil {
|
|
return new_buckets, err
|
|
}
|
|
|
|
if exists {
|
|
continue
|
|
}
|
|
|
|
if err = m.MinioClient.MakeBucket(context.Background(), bucket, minio.MakeBucketOptions{}); err != nil {
|
|
return new_buckets, err
|
|
}
|
|
new_buckets = append(new_buckets, bucket)
|
|
}
|
|
|
|
return new_buckets, nil
|
|
}
|
|
|
|
func (m *MediaClient) UploadFormFiles(bucketName string, fileHeaders []*multipart.FileHeader) (statusCode int, result string) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
ok, err := m.MinioClient.BucketExists(ctx, bucketName)
|
|
if err != nil {
|
|
return http.StatusInternalServerError, fmt.Sprintf("Erreur lors de vérification d'existence de bucket '%s': %s", bucketName, err)
|
|
}
|
|
if !ok {
|
|
return http.StatusBadRequest, fmt.Sprintf("Bucket '%s' n'existe pas", bucketName)
|
|
}
|
|
|
|
switch count := len(fileHeaders); count {
|
|
case 0:
|
|
return http.StatusBadRequest, "Veuillez sélectionner au moins 1 document à téléverser"
|
|
case 1:
|
|
result = "Téléversement de 1 fichier\n"
|
|
default:
|
|
result = fmt.Sprintf("Téléversement de %d fichiers\n", count)
|
|
}
|
|
|
|
var allowedMediaTypes = []string{"application/pdf", "text/markdown", "text/plain"}
|
|
|
|
var fileNames []string
|
|
for _, fileHeader := range fileHeaders {
|
|
fileNames = append(fileNames, fileHeader.Filename)
|
|
}
|
|
|
|
var validFileHeaders []*multipart.FileHeader
|
|
|
|
for i, fileHeader := range fileHeaders {
|
|
// Check for conflicting file names in upload
|
|
for j, fileName := range fileNames {
|
|
if fileName == fileHeader.Filename && i != j {
|
|
return http.StatusBadRequest, fmt.Sprintf("Doublon de nom de fichier '%s' trouvé, les noms de fichiers doivent être uniques", fileName)
|
|
}
|
|
}
|
|
|
|
// Check media type
|
|
mediaType, _, err := mime.ParseMediaType(fileHeader.Header.Get("Content-Type"))
|
|
if err != nil {
|
|
return http.StatusBadRequest, fmt.Sprintf("Impossible de déterminer le type de fichier pour %d '%s'.\nPlus de détails: %s", i, fileHeader.Filename, err.Error())
|
|
}
|
|
|
|
var isAllowedMediaType bool
|
|
|
|
for _, allowedMediaType := range allowedMediaTypes {
|
|
if allowedMediaType == mediaType {
|
|
isAllowedMediaType = true
|
|
}
|
|
}
|
|
|
|
if !isAllowedMediaType {
|
|
return http.StatusUnsupportedMediaType, fmt.Sprintf("Type de fichier interdit '%s' pour '%s'.\nTypes de fichiers permis: %s", mediaType, fileHeader.Filename, allowedMediaTypes)
|
|
}
|
|
|
|
// Check for conflicting fileNames with existing files
|
|
objectInfo, err := m.MinioClient.StatObject(ctx, bucketName, fileHeader.Filename, minio.StatObjectOptions{})
|
|
if err == nil && objectInfo.Key == fileHeader.Filename {
|
|
return http.StatusConflict, fmt.Sprintf("Un document au nom '%s' de catégorie '%s' existe déjà et ne peut pas être inséré de cette façon.", fileHeader.Filename, bucketName)
|
|
}
|
|
|
|
switch msg := err.Error(); msg {
|
|
case "The specified key does not exist.":
|
|
default:
|
|
return http.StatusInternalServerError, fmt.Sprintf("Erreur inattendue lors de vérification de conflit de nom de fichier avec la base de données: %s", err)
|
|
}
|
|
|
|
validFileHeaders = append(validFileHeaders, fileHeader)
|
|
}
|
|
|
|
if len(validFileHeaders) == 0 {
|
|
return http.StatusOK, "Aucun fichier valide envoyé au serveur, rien à faire."
|
|
}
|
|
|
|
for i, fileHeader := range validFileHeaders {
|
|
mediaType, _, err := mime.ParseMediaType(fileHeader.Header.Get("Content-Type"))
|
|
if err != nil {
|
|
return http.StatusBadRequest, fmt.Sprintf("Impossible de déterminer le type de fichier pour %d '%s'.\nPlus de détails: %s", i, fileHeader.Filename, err.Error())
|
|
}
|
|
|
|
// Get file content
|
|
fileContent, err := fileHeader.Open()
|
|
if err != nil {
|
|
return http.StatusBadRequest, fmt.Sprintf("Impossible de lire le contenu de '%s': %s", fileHeader.Filename, err)
|
|
}
|
|
defer fileContent.Close()
|
|
|
|
// Upload file
|
|
info, err := m.MinioClient.PutObject(ctx, bucketName, fileHeader.Filename, fileContent, fileHeader.Size, minio.PutObjectOptions{
|
|
ContentType: mediaType,
|
|
})
|
|
if err != nil {
|
|
return http.StatusInternalServerError, fmt.Sprintf("Impossible d'ajouter '%s' à la base de donnée: %s", fileHeader.Filename, err)
|
|
}
|
|
|
|
result = fmt.Sprintf("%sDocument %d '%s' de type '%s' et de taille '%d' téléversé à '%s' avec succès\n",
|
|
result, i, info.Key, mediaType, info.Size, info.Bucket)
|
|
}
|
|
|
|
return http.StatusCreated, result
|
|
}
|