1a56070556
- Introduced a comprehensive MinIO package for managing object storage, including support for hierarchical document storage and general file management. - Implemented core components such as Config, ConnectionManager, and Service, following Clean Architecture principles. - Added functionality for file uploads, downloads, bucket management, and structured logging, enhancing usability and maintainability. - Included detailed error handling and validation for configuration and operations, ensuring robustness. - Updated documentation with usage examples and configuration guidelines to facilitate integration and understanding of the package's functionality.
253 lines
6.3 KiB
Go
253 lines
6.3 KiB
Go
package minio
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
|
|
"github.com/minio/minio-go/v7"
|
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
|
)
|
|
|
|
// ConnectionManager manages MinIO connections and operations
|
|
type ConnectionManager struct {
|
|
client *minio.Client
|
|
config Config
|
|
logger Logger
|
|
}
|
|
|
|
// NewConnectionManager creates a new MinIO connection manager
|
|
func NewConnectionManager(config Config, logger Logger) (*ConnectionManager, error) {
|
|
if err := config.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ConnectionManager{
|
|
config: config,
|
|
logger: logger,
|
|
}, nil
|
|
}
|
|
|
|
// Connect establishes connection to MinIO
|
|
func (cm *ConnectionManager) Connect() error {
|
|
// Create MinIO client
|
|
minioClient, err := minio.New(cm.config.Endpoint, &minio.Options{
|
|
Creds: credentials.NewStaticV4(cm.config.AccessKeyID, cm.config.SecretAccessKey, ""),
|
|
Secure: cm.config.UseSSL,
|
|
Region: cm.config.Region,
|
|
Transport: &http.Transport{
|
|
ResponseHeaderTimeout: cm.config.RequestTimeout,
|
|
},
|
|
})
|
|
if err != nil {
|
|
cm.logger.Error("Failed to create MinIO client", map[string]interface{}{
|
|
"endpoint": cm.config.Endpoint,
|
|
"error": err.Error(),
|
|
})
|
|
return NewErrorWithCause(ErrCodeConnectionFailed, "failed to create MinIO client", err)
|
|
}
|
|
|
|
cm.client = minioClient
|
|
|
|
// Test connection by listing buckets
|
|
ctx, cancel := context.WithTimeout(context.Background(), cm.config.ConnectTimeout)
|
|
defer cancel()
|
|
|
|
_, err = cm.client.ListBuckets(ctx)
|
|
if err != nil {
|
|
cm.logger.Error("Failed to connect to MinIO", map[string]interface{}{
|
|
"endpoint": cm.config.Endpoint,
|
|
"error": err.Error(),
|
|
})
|
|
return NewErrorWithCause(ErrCodeConnectionFailed, "failed to connect to MinIO", err)
|
|
}
|
|
|
|
cm.logger.Info("Successfully connected to MinIO", map[string]interface{}{
|
|
"endpoint": cm.config.Endpoint,
|
|
"region": cm.config.Region,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// Disconnect closes the MinIO connection
|
|
func (cm *ConnectionManager) Disconnect(ctx context.Context) error {
|
|
if cm.client == nil {
|
|
return nil
|
|
}
|
|
|
|
// MinIO client doesn't have a disconnect method, but we can set it to nil
|
|
cm.client = nil
|
|
|
|
cm.logger.Info("Disconnected from MinIO", nil)
|
|
return nil
|
|
}
|
|
|
|
// Ping tests the MinIO connection
|
|
func (cm *ConnectionManager) Ping(ctx context.Context) error {
|
|
if cm.client == nil {
|
|
return ErrNotConnected
|
|
}
|
|
|
|
_, err := cm.client.ListBuckets(ctx)
|
|
if err != nil {
|
|
cm.logger.Error("MinIO ping failed", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return NewErrorWithCause(ErrCodeConnectionFailed, "ping failed", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsConnected returns true if connected to MinIO
|
|
func (cm *ConnectionManager) IsConnected() bool {
|
|
return cm.client != nil
|
|
}
|
|
|
|
// GetClient returns the underlying MinIO client
|
|
func (cm *ConnectionManager) GetClient() interface{} {
|
|
return cm.client
|
|
}
|
|
|
|
// CreateBucket creates a new bucket if it doesn't exist
|
|
func (cm *ConnectionManager) CreateBucket(ctx context.Context, bucketName string) error {
|
|
if cm.client == nil {
|
|
return ErrNotConnected
|
|
}
|
|
|
|
if bucketName == "" {
|
|
return ErrInvalidBucketName
|
|
}
|
|
|
|
exists, err := cm.client.BucketExists(ctx, bucketName)
|
|
if err != nil {
|
|
cm.logger.Error("Failed to check bucket existence", map[string]interface{}{
|
|
"bucket": bucketName,
|
|
"error": err.Error(),
|
|
})
|
|
return NewErrorWithCause(ErrCodeBucketNotFound, "failed to check bucket existence", err)
|
|
}
|
|
|
|
if exists {
|
|
cm.logger.Debug("Bucket already exists", map[string]interface{}{
|
|
"bucket": bucketName,
|
|
})
|
|
return nil
|
|
}
|
|
|
|
err = cm.client.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{
|
|
Region: cm.config.Region,
|
|
})
|
|
if err != nil {
|
|
cm.logger.Error("Failed to create bucket", map[string]interface{}{
|
|
"bucket": bucketName,
|
|
"error": err.Error(),
|
|
})
|
|
return NewErrorWithCause("BUCKET_CREATE_FAILED", "failed to create bucket", err)
|
|
}
|
|
|
|
cm.logger.Info("Bucket created successfully", map[string]interface{}{
|
|
"bucket": bucketName,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeleteBucket deletes a bucket
|
|
func (cm *ConnectionManager) DeleteBucket(ctx context.Context, bucketName string) error {
|
|
if cm.client == nil {
|
|
return ErrNotConnected
|
|
}
|
|
|
|
if bucketName == "" {
|
|
return ErrInvalidBucketName
|
|
}
|
|
|
|
err := cm.client.RemoveBucket(ctx, bucketName)
|
|
if err != nil {
|
|
cm.logger.Error("Failed to delete bucket", map[string]interface{}{
|
|
"bucket": bucketName,
|
|
"error": err.Error(),
|
|
})
|
|
return NewErrorWithCause("BUCKET_DELETE_FAILED", "failed to delete bucket", err)
|
|
}
|
|
|
|
cm.logger.Info("Bucket deleted successfully", map[string]interface{}{
|
|
"bucket": bucketName,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// BucketExists checks if a bucket exists
|
|
func (cm *ConnectionManager) BucketExists(ctx context.Context, bucketName string) (bool, error) {
|
|
if cm.client == nil {
|
|
return false, ErrNotConnected
|
|
}
|
|
|
|
if bucketName == "" {
|
|
return false, ErrInvalidBucketName
|
|
}
|
|
|
|
exists, err := cm.client.BucketExists(ctx, bucketName)
|
|
if err != nil {
|
|
cm.logger.Error("Failed to check bucket existence", map[string]interface{}{
|
|
"bucket": bucketName,
|
|
"error": err.Error(),
|
|
})
|
|
return false, NewErrorWithCause(ErrCodeBucketNotFound, "failed to check bucket existence", err)
|
|
}
|
|
|
|
return exists, nil
|
|
}
|
|
|
|
// ListBuckets lists all buckets
|
|
func (cm *ConnectionManager) ListBuckets(ctx context.Context) ([]string, error) {
|
|
if cm.client == nil {
|
|
return nil, ErrNotConnected
|
|
}
|
|
|
|
buckets, err := cm.client.ListBuckets(ctx)
|
|
if err != nil {
|
|
cm.logger.Error("Failed to list buckets", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, NewErrorWithCause("LIST_BUCKETS_FAILED", "failed to list buckets", err)
|
|
}
|
|
|
|
bucketNames := make([]string, len(buckets))
|
|
for i, bucket := range buckets {
|
|
bucketNames[i] = bucket.Name
|
|
}
|
|
|
|
cm.logger.Debug("Buckets listed successfully", map[string]interface{}{
|
|
"count": len(bucketNames),
|
|
})
|
|
|
|
return bucketNames, nil
|
|
}
|
|
|
|
// GetBucketInfo gets bucket information
|
|
func (cm *ConnectionManager) GetBucketInfo(ctx context.Context, bucketName string) (map[string]interface{}, error) {
|
|
if cm.client == nil {
|
|
return nil, ErrNotConnected
|
|
}
|
|
|
|
if bucketName == "" {
|
|
return nil, ErrInvalidBucketName
|
|
}
|
|
|
|
// For now, return basic bucket info
|
|
// In a real implementation, you might want to get more detailed info
|
|
info := map[string]interface{}{
|
|
"name": bucketName,
|
|
"region": cm.config.Region,
|
|
}
|
|
|
|
cm.logger.Debug("Bucket info retrieved", map[string]interface{}{
|
|
"bucket": bucketName,
|
|
})
|
|
|
|
return info, nil
|
|
}
|