qudoco-be/utils/storage/cms_upload.go

101 lines
3.1 KiB
Go
Raw Normal View History

2026-04-10 07:23:24 +00:00
package storage
import (
"context"
"fmt"
"mime"
"mime/multipart"
"path/filepath"
"strings"
"time"
"github.com/google/uuid"
"github.com/minio/minio-go/v7"
appcfg "web-qudo-be/config/config"
)
// CMSPreviewURL is the absolute URL served by this API (GET /cms-media/viewer/...) for DB image_url / media_url fields.
func CMSPreviewURL(cfg *appcfg.Config, objectKey string) string {
base := cfg.APIPublicBaseURL()
key := strings.TrimPrefix(strings.TrimSpace(objectKey), "/")
return base + "/cms-media/viewer/" + key
}
2026-04-10 07:23:24 +00:00
var imageExts = map[string]bool{
".jpg": true, ".jpeg": true, ".png": true, ".gif": true, ".webp": true,
}
var mediaExts = map[string]bool{
".jpg": true, ".jpeg": true, ".png": true, ".gif": true, ".webp": true,
".mp4": true, ".webm": true,
}
// mediaLibraryExts = images + video + audio + common documents (admin Media Library upload).
var mediaLibraryExts = map[string]bool{
".jpg": true, ".jpeg": true, ".png": true, ".gif": true, ".webp": true, ".svg": true,
".mp4": true, ".webm": true, ".mov": true,
".mp3": true, ".wav": true, ".ogg": true, ".m4a": true,
".pdf": true, ".doc": true, ".docx": true, ".txt": true, ".csv": true,
}
// UploadCMSObject stores a file in MinIO under cms/{folder}/YYYY/MM/{uuid}{ext} and returns object key + preview URL (API viewer, not direct MinIO).
func UploadCMSObject(ms *appcfg.MinioStorage, folder string, file *multipart.FileHeader, allowVideo bool) (objectKey string, previewURL string, err error) {
2026-04-10 07:23:24 +00:00
if file == nil {
return "", "", fmt.Errorf("file is required")
}
ext := strings.ToLower(filepath.Ext(file.Filename))
if folder == "media-library" {
if !mediaLibraryExts[ext] {
return "", "", fmt.Errorf("unsupported file type for media library")
}
} else if allowVideo {
2026-04-10 07:23:24 +00:00
if !mediaExts[ext] {
return "", "", fmt.Errorf("unsupported file type (allowed: images, mp4, webm)")
}
} else if !imageExts[ext] {
return "", "", fmt.Errorf("unsupported image type")
}
client, err := ms.ConnectMinio()
if err != nil {
return "", "", err
}
src, err := file.Open()
if err != nil {
return "", "", err
}
defer src.Close()
bucket := ms.Cfg.ObjectStorage.MinioStorage.BucketName
now := time.Now()
objectKey = fmt.Sprintf("cms/%s/%d/%02d/%s%s", folder, now.Year(), int(now.Month()), uuid.New().String(), ext)
contentType := mime.TypeByExtension(ext)
if contentType == "" {
contentType = "application/octet-stream"
}
_, err = client.PutObject(context.Background(), bucket, objectKey, src, file.Size, minio.PutObjectOptions{
ContentType: contentType,
})
if err != nil {
return "", "", err
}
return objectKey, CMSPreviewURL(ms.Cfg, objectKey), nil
2026-04-10 07:23:24 +00:00
}
// UploadMediaLibraryObject stores under cms/media-library/... with a broader MIME allowlist.
func UploadMediaLibraryObject(ms *appcfg.MinioStorage, file *multipart.FileHeader) (objectKey string, previewURL string, err error) {
if file == nil {
return "", "", fmt.Errorf("file is required")
}
ext := strings.ToLower(filepath.Ext(file.Filename))
if !mediaLibraryExts[ext] {
return "", "", fmt.Errorf("unsupported file type for media library")
}
return UploadCMSObject(ms, "media-library", file, false)
}