93 lines
2.7 KiB
Go
93 lines
2.7 KiB
Go
package biz
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type MediaRepo interface {
|
|
MediaMetadataRepo
|
|
UploadRepo
|
|
}
|
|
|
|
type MediaUsecase struct {
|
|
MediaRepo
|
|
files FileStorage
|
|
}
|
|
|
|
func NewMediaUsecase(repo MediaRepo, files FileStorage) *MediaUsecase {
|
|
return &MediaUsecase{MediaRepo: repo, files: files}
|
|
}
|
|
|
|
var allowedMediaExtensions = map[string]bool{
|
|
".jpg": true, ".jpeg": true, ".png": true, ".gif": true, ".webp": true, ".bmp": true, ".ico": true, ".avif": true,
|
|
".mp3": true, ".wav": true, ".ogg": true, ".m4a": true, ".flac": true, ".aac": true,
|
|
".mp4": true, ".webm": true, ".mov": true, ".avi": true, ".mkv": true,
|
|
".txt": true, ".md": true, ".csv": true, ".json": true, ".log": true, ".pdf": true,
|
|
".doc": true, ".docx": true, ".xls": true, ".xlsx": true, ".ppt": true, ".pptx": true,
|
|
".zip": true, ".rar": true, ".7z": true, ".tar": true, ".gz": true, ".tgz": true, ".bin": true,
|
|
}
|
|
|
|
func validateMediaName(name string) error {
|
|
if name == "" || strings.TrimSpace(name) != name || strings.ContainsAny(name, `/\`) {
|
|
return errors.New("file extension is not allowed")
|
|
}
|
|
if !allowedMediaExtensions[strings.ToLower(filepath.Ext(name))] {
|
|
return errors.New("file extension is not allowed")
|
|
}
|
|
return nil
|
|
}
|
|
func (uc *MediaUsecase) Upload(ctx context.Context, userID uint, name, mime string, categoryID int, reader io.Reader, save bool) (*MediaFile, error) {
|
|
if err := validateMediaName(name); err != nil {
|
|
return nil, err
|
|
}
|
|
ext := strings.ToLower(filepath.Ext(name))
|
|
buffered := bufio.NewReader(reader)
|
|
if header, _ := buffered.Peek(512); len(header) > 0 {
|
|
mime = http.DetectContentType(header)
|
|
}
|
|
key := time.Now().Format("20060102") + "/" + uuid.NewString() + ext
|
|
hash := md5.New()
|
|
stored, err := uc.files.Put(ctx, key, io.TeeReader(buffered, hash))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
media := &MediaFile{Name: name, CategoryID: categoryID, URL: stored.URL, Tag: strings.TrimPrefix(ext, "."), Key: key, Size: stored.Size, Mime: mime, MD5: hex.EncodeToString(hash.Sum(nil)), UserID: userID}
|
|
if save {
|
|
if err = uc.CreateMedia(ctx, media); err != nil {
|
|
_ = uc.files.Delete(ctx, key)
|
|
return nil, err
|
|
}
|
|
}
|
|
return media, nil
|
|
}
|
|
func (uc *MediaUsecase) Delete(ctx context.Context, id uint) error {
|
|
media, err := uc.FindMedia(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
count, err := uc.MediaKeyReferences(ctx, media.Key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if count <= 1 {
|
|
if err = uc.files.Delete(ctx, media.Key); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return uc.DeleteMedia(ctx, id)
|
|
}
|
|
func (uc *MediaUsecase) ListStorage(ctx context.Context, prefix, cursor string, limit int) ([]*StoredFile, string, bool, error) {
|
|
return uc.files.List(ctx, prefix, cursor, limit)
|
|
}
|