Add MinIO Package for Object Storage Management
- 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.
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user