kontenhumas-be/app/module/clients/service/client_logo_upload.service.go

197 lines
5.1 KiB
Go

package service
import (
"context"
"fmt"
"math/rand"
"path/filepath"
"strings"
"time"
"netidhub-saas-be/config/config"
"github.com/gofiber/fiber/v2"
"github.com/minio/minio-go/v7"
"github.com/rs/zerolog"
)
// ClientLogoUploadService handles client logo uploads to MinIO
type ClientLogoUploadService struct {
MinioStorage *config.MinioStorage
Log zerolog.Logger
}
// NewClientLogoUploadService creates a new client logo upload service
func NewClientLogoUploadService(minioStorage *config.MinioStorage, log zerolog.Logger) *ClientLogoUploadService {
return &ClientLogoUploadService{
MinioStorage: minioStorage,
Log: log,
}
}
// UploadLogo uploads client logo to MinIO and returns the image path
func (s *ClientLogoUploadService) UploadLogo(c *fiber.Ctx, clientId string) (string, error) {
// Get the uploaded file
file, err := c.FormFile("logo")
if err != nil {
return "", fmt.Errorf("failed to get uploaded file: %w", err)
}
// Validate file type
if !s.isValidImageType(file.Filename) {
return "", fmt.Errorf("invalid file type. Only jpg, jpeg, png, gif, webp are allowed")
}
// Validate file size (max 5MB)
const maxSize = 5 * 1024 * 1024 // 5MB
if file.Size > maxSize {
return "", fmt.Errorf("file size too large. Maximum size is 5MB")
}
// Create MinIO connection
minioClient, err := s.MinioStorage.ConnectMinio()
if err != nil {
return "", fmt.Errorf("failed to connect to MinIO: %w", err)
}
bucketName := s.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
// Generate unique filename
filename := s.generateUniqueFilename(file.Filename)
objectName := fmt.Sprintf("clients/logos/%s/%s", clientId, filename)
// Open file
src, err := file.Open()
if err != nil {
return "", fmt.Errorf("failed to open uploaded file: %w", err)
}
defer src.Close()
// Upload to MinIO
_, err = minioClient.PutObject(
context.Background(),
bucketName,
objectName,
src,
file.Size,
minio.PutObjectOptions{
ContentType: s.getContentType(file.Filename),
},
)
if err != nil {
return "", fmt.Errorf("failed to upload file to MinIO: %w", err)
}
s.Log.Info().
Str("clientId", clientId).
Str("filename", filename).
Str("objectName", objectName).
Int64("fileSize", file.Size).
Msg("Client logo uploaded successfully")
return objectName, nil
}
// DeleteLogo deletes client logo from MinIO
func (s *ClientLogoUploadService) DeleteLogo(clientId, imagePath string) error {
if imagePath == "" {
return nil // Nothing to delete
}
// Create MinIO connection
minioClient, err := s.MinioStorage.ConnectMinio()
if err != nil {
return fmt.Errorf("failed to connect to MinIO: %w", err)
}
bucketName := s.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
// Delete from MinIO
err = minioClient.RemoveObject(context.Background(), bucketName, imagePath, minio.RemoveObjectOptions{})
if err != nil {
return fmt.Errorf("failed to delete file from MinIO: %w", err)
}
s.Log.Info().
Str("clientId", clientId).
Str("imagePath", imagePath).
Msg("Client logo deleted successfully")
return nil
}
// GetLogoURL generates a presigned URL for the logo
func (s *ClientLogoUploadService) GetLogoURL(imagePath string, expiry time.Duration) (string, error) {
if imagePath == "" {
return "", nil
}
// Create MinIO connection
minioClient, err := s.MinioStorage.ConnectMinio()
if err != nil {
return "", fmt.Errorf("failed to connect to MinIO: %w", err)
}
bucketName := s.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
// Generate presigned URL
url, err := minioClient.PresignedGetObject(context.Background(), bucketName, imagePath, expiry, nil)
if err != nil {
return "", fmt.Errorf("failed to generate presigned URL: %w", err)
}
return url.String(), nil
}
// isValidImageType checks if the file extension is a valid image type
func (s *ClientLogoUploadService) isValidImageType(filename string) bool {
ext := strings.ToLower(filepath.Ext(filename))
validExts := []string{".jpg", ".jpeg", ".png", ".gif", ".webp"}
for _, validExt := range validExts {
if ext == validExt {
return true
}
}
return false
}
// generateUniqueFilename generates a unique filename with timestamp and random number
func (s *ClientLogoUploadService) generateUniqueFilename(originalFilename string) string {
ext := filepath.Ext(originalFilename)
nameWithoutExt := strings.TrimSuffix(filepath.Base(originalFilename), ext)
// Clean filename (remove spaces and special characters)
nameWithoutExt = strings.ReplaceAll(nameWithoutExt, " ", "_")
nameWithoutExt = strings.ReplaceAll(nameWithoutExt, "-", "_")
// Generate unique suffix
now := time.Now()
rand.Seed(now.UnixNano())
randomNum := rand.Intn(1000000)
// Format: originalname_timestamp_random.ext
return fmt.Sprintf("%s_%d_%d%s",
nameWithoutExt,
now.Unix(),
randomNum,
ext)
}
// getContentType returns the MIME type based on file extension
func (s *ClientLogoUploadService) getContentType(filename string) string {
ext := strings.ToLower(filepath.Ext(filename))
switch ext {
case ".jpg", ".jpeg":
return "image/jpeg"
case ".png":
return "image/png"
case ".gif":
return "image/gif"
case ".webp":
return "image/webp"
default:
return "application/octet-stream"
}
}