package storage import ( "context" "fmt" "io" "net/url" "time" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" "github.com/youruser/base/internal/config" ) type MinIOStorage struct { client *minio.Client bucket string } func NewMinIOStorage(cfg config.MinIOStorageConfig) (*MinIOStorage, error) { if cfg.Endpoint == "" || cfg.AccessKeyId == "" || cfg.AccessKeySecret == "" || cfg.Bucket == "" { return nil, fmt.Errorf("MinIO config incomplete: endpoint, accessKeyId, accessKeySecret, bucket are required") } client, err := minio.New(cfg.Endpoint, &minio.Options{ Creds: credentials.NewStaticV4(cfg.AccessKeyId, cfg.AccessKeySecret, ""), Secure: cfg.UseSSL, }) if err != nil { return nil, fmt.Errorf("failed to create MinIO client: %w", err) } ctx := context.Background() exists, err := client.BucketExists(ctx, cfg.Bucket) if err != nil { return nil, fmt.Errorf("failed to check MinIO bucket: %w", err) } if !exists { if err := client.MakeBucket(ctx, cfg.Bucket, minio.MakeBucketOptions{}); err != nil { return nil, fmt.Errorf("failed to create MinIO bucket: %w", err) } } return &MinIOStorage{client: client, bucket: cfg.Bucket}, nil } func (s *MinIOStorage) Upload(ctx context.Context, key string, reader io.Reader, size int64, contentType string) error { opts := minio.PutObjectOptions{ ContentType: contentType, } if _, err := s.client.PutObject(ctx, s.bucket, key, reader, size, opts); err != nil { return fmt.Errorf("failed to upload to MinIO: %w", err) } return nil } func (s *MinIOStorage) Delete(ctx context.Context, key string) error { if err := s.client.RemoveObject(ctx, s.bucket, key, minio.RemoveObjectOptions{}); err != nil { return fmt.Errorf("failed to delete from MinIO: %w", err) } return nil } func (s *MinIOStorage) GetURL(ctx context.Context, key string) (string, error) { reqParams := make(url.Values) presignedURL, err := s.client.PresignedGetObject(ctx, s.bucket, key, time.Hour, reqParams) if err != nil { return "", fmt.Errorf("failed to generate MinIO presigned URL: %w", err) } return presignedURL.String(), nil } func (s *MinIOStorage) Exists(ctx context.Context, key string) (bool, error) { _, err := s.client.StatObject(ctx, s.bucket, key, minio.StatObjectOptions{}) if err != nil { errResponse := minio.ToErrorResponse(err) if errResponse.Code == "NoSuchKey" { return false, nil } return false, fmt.Errorf("failed to check MinIO object: %w", err) } return true, nil }