add function to s3
This commit is contained in:
297
backend/internal/object_storage/service.go
Normal file
297
backend/internal/object_storage/service.go
Normal file
@@ -0,0 +1,297 @@
|
||||
package object_storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/atlasos/calypso/internal/common/logger"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
madmin "github.com/minio/madmin-go/v3"
|
||||
)
|
||||
|
||||
// Service handles MinIO object storage operations
|
||||
type Service struct {
|
||||
client *minio.Client
|
||||
adminClient *madmin.AdminClient
|
||||
logger *logger.Logger
|
||||
endpoint string
|
||||
accessKey string
|
||||
secretKey string
|
||||
}
|
||||
|
||||
// NewService creates a new MinIO service
|
||||
func NewService(endpoint, accessKey, secretKey string, log *logger.Logger) (*Service, error) {
|
||||
// Create MinIO client
|
||||
minioClient, err := minio.New(endpoint, &minio.Options{
|
||||
Creds: credentials.NewStaticV4(accessKey, secretKey, ""),
|
||||
Secure: false, // Set to true if using HTTPS
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create MinIO client: %w", err)
|
||||
}
|
||||
|
||||
// Create MinIO Admin client
|
||||
adminClient, err := madmin.New(endpoint, accessKey, secretKey, false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create MinIO admin client: %w", err)
|
||||
}
|
||||
|
||||
return &Service{
|
||||
client: minioClient,
|
||||
adminClient: adminClient,
|
||||
logger: log,
|
||||
endpoint: endpoint,
|
||||
accessKey: accessKey,
|
||||
secretKey: secretKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Bucket represents a MinIO bucket
|
||||
type Bucket struct {
|
||||
Name string `json:"name"`
|
||||
CreationDate time.Time `json:"creation_date"`
|
||||
Size int64 `json:"size"` // Total size in bytes
|
||||
Objects int64 `json:"objects"` // Number of objects
|
||||
AccessPolicy string `json:"access_policy"` // private, public-read, public-read-write
|
||||
}
|
||||
|
||||
// ListBuckets lists all buckets in MinIO
|
||||
func (s *Service) ListBuckets(ctx context.Context) ([]*Bucket, error) {
|
||||
buckets, err := s.client.ListBuckets(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list buckets: %w", err)
|
||||
}
|
||||
|
||||
result := make([]*Bucket, 0, len(buckets))
|
||||
for _, bucket := range buckets {
|
||||
bucketInfo, err := s.getBucketInfo(ctx, bucket.Name)
|
||||
if err != nil {
|
||||
s.logger.Warn("Failed to get bucket info", "bucket", bucket.Name, "error", err)
|
||||
// Continue with basic info
|
||||
result = append(result, &Bucket{
|
||||
Name: bucket.Name,
|
||||
CreationDate: bucket.CreationDate,
|
||||
Size: 0,
|
||||
Objects: 0,
|
||||
AccessPolicy: "private",
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, bucketInfo)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// getBucketInfo gets detailed information about a bucket
|
||||
func (s *Service) getBucketInfo(ctx context.Context, bucketName string) (*Bucket, error) {
|
||||
// Get bucket creation date
|
||||
buckets, err := s.client.ListBuckets(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var creationDate time.Time
|
||||
for _, b := range buckets {
|
||||
if b.Name == bucketName {
|
||||
creationDate = b.CreationDate
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Get bucket size and object count by listing objects
|
||||
var size int64
|
||||
var objects int64
|
||||
|
||||
// List objects in bucket to calculate size and count
|
||||
objectCh := s.client.ListObjects(ctx, bucketName, minio.ListObjectsOptions{
|
||||
Recursive: true,
|
||||
})
|
||||
|
||||
for object := range objectCh {
|
||||
if object.Err != nil {
|
||||
s.logger.Warn("Error listing object", "bucket", bucketName, "error", object.Err)
|
||||
continue
|
||||
}
|
||||
objects++
|
||||
size += object.Size
|
||||
}
|
||||
|
||||
return &Bucket{
|
||||
Name: bucketName,
|
||||
CreationDate: creationDate,
|
||||
Size: size,
|
||||
Objects: objects,
|
||||
AccessPolicy: s.getBucketPolicy(ctx, bucketName),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getBucketPolicy gets the access policy for a bucket
|
||||
func (s *Service) getBucketPolicy(ctx context.Context, bucketName string) string {
|
||||
policy, err := s.client.GetBucketPolicy(ctx, bucketName)
|
||||
if err != nil {
|
||||
return "private"
|
||||
}
|
||||
|
||||
// Parse policy JSON to determine access type
|
||||
// For simplicity, check if policy allows public read
|
||||
if policy != "" {
|
||||
// Check if policy contains public read access
|
||||
if strings.Contains(policy, "s3:GetObject") && strings.Contains(policy, "Principal") && strings.Contains(policy, "*") {
|
||||
if strings.Contains(policy, "s3:PutObject") {
|
||||
return "public-read-write"
|
||||
}
|
||||
return "public-read"
|
||||
}
|
||||
}
|
||||
|
||||
return "private"
|
||||
}
|
||||
|
||||
|
||||
// CreateBucket creates a new bucket
|
||||
func (s *Service) CreateBucket(ctx context.Context, bucketName string) error {
|
||||
err := s.client.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create bucket: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteBucket deletes a bucket
|
||||
func (s *Service) DeleteBucket(ctx context.Context, bucketName string) error {
|
||||
err := s.client.RemoveBucket(ctx, bucketName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete bucket: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBucketStats gets statistics for a bucket
|
||||
func (s *Service) GetBucketStats(ctx context.Context, bucketName string) (*Bucket, error) {
|
||||
return s.getBucketInfo(ctx, bucketName)
|
||||
}
|
||||
|
||||
// User represents a MinIO IAM user
|
||||
type User struct {
|
||||
AccessKey string `json:"access_key"`
|
||||
Status string `json:"status"` // "enabled" or "disabled"
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// ListUsers lists all IAM users in MinIO
|
||||
func (s *Service) ListUsers(ctx context.Context) ([]*User, error) {
|
||||
users, err := s.adminClient.ListUsers(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list users: %w", err)
|
||||
}
|
||||
|
||||
result := make([]*User, 0, len(users))
|
||||
for accessKey, userInfo := range users {
|
||||
status := "enabled"
|
||||
if userInfo.Status == madmin.AccountDisabled {
|
||||
status = "disabled"
|
||||
}
|
||||
|
||||
// MinIO doesn't provide creation date, use current time
|
||||
result = append(result, &User{
|
||||
AccessKey: accessKey,
|
||||
Status: status,
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CreateUser creates a new IAM user in MinIO
|
||||
func (s *Service) CreateUser(ctx context.Context, accessKey, secretKey string) error {
|
||||
err := s.adminClient.AddUser(ctx, accessKey, secretKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create user: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteUser deletes an IAM user from MinIO
|
||||
func (s *Service) DeleteUser(ctx context.Context, accessKey string) error {
|
||||
err := s.adminClient.RemoveUser(ctx, accessKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete user: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServiceAccount represents a MinIO service account (access key)
|
||||
type ServiceAccount struct {
|
||||
AccessKey string `json:"access_key"`
|
||||
SecretKey string `json:"secret_key,omitempty"` // Only returned on creation
|
||||
ParentUser string `json:"parent_user"`
|
||||
Expiration time.Time `json:"expiration,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// ListServiceAccounts lists all service accounts in MinIO
|
||||
func (s *Service) ListServiceAccounts(ctx context.Context) ([]*ServiceAccount, error) {
|
||||
accounts, err := s.adminClient.ListServiceAccounts(ctx, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list service accounts: %w", err)
|
||||
}
|
||||
|
||||
result := make([]*ServiceAccount, 0, len(accounts.Accounts))
|
||||
for _, account := range accounts.Accounts {
|
||||
var expiration time.Time
|
||||
if account.Expiration != nil {
|
||||
expiration = *account.Expiration
|
||||
}
|
||||
|
||||
result = append(result, &ServiceAccount{
|
||||
AccessKey: account.AccessKey,
|
||||
ParentUser: account.ParentUser,
|
||||
Expiration: expiration,
|
||||
CreatedAt: time.Now(), // MinIO doesn't provide creation date
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CreateServiceAccount creates a new service account (access key) in MinIO
|
||||
func (s *Service) CreateServiceAccount(ctx context.Context, parentUser string, policy string, expiration *time.Time) (*ServiceAccount, error) {
|
||||
opts := madmin.AddServiceAccountReq{
|
||||
TargetUser: parentUser,
|
||||
}
|
||||
if policy != "" {
|
||||
opts.Policy = json.RawMessage(policy)
|
||||
}
|
||||
if expiration != nil {
|
||||
opts.Expiration = expiration
|
||||
}
|
||||
|
||||
creds, err := s.adminClient.AddServiceAccount(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create service account: %w", err)
|
||||
}
|
||||
|
||||
return &ServiceAccount{
|
||||
AccessKey: creds.AccessKey,
|
||||
SecretKey: creds.SecretKey,
|
||||
ParentUser: parentUser,
|
||||
Expiration: creds.Expiration,
|
||||
CreatedAt: time.Now(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteServiceAccount deletes a service account from MinIO
|
||||
func (s *Service) DeleteServiceAccount(ctx context.Context, accessKey string) error {
|
||||
err := s.adminClient.DeleteServiceAccount(ctx, accessKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete service account: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user