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"
|
|
|
|
|
)
|
|
|
|
|
|
2026-04-10 08:11:47 +00:00
|
|
|
// 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,
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-10 08:11:47 +00:00
|
|
|
// 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 allowVideo {
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-10 08:11:47 +00:00
|
|
|
return objectKey, CMSPreviewURL(ms.Cfg, objectKey), nil
|
2026-04-10 07:23:24 +00:00
|
|
|
}
|