77 lines
2.2 KiB
Go
77 lines
2.2 KiB
Go
// Package s3 wraps the MinIO client to provide object storage for datasets.
|
|
package s3
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
|
|
"gis/internal/config"
|
|
|
|
"github.com/minio/minio-go/v7"
|
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
|
)
|
|
|
|
// Client stores and retrieves dataset objects in an S3-compatible bucket.
|
|
type Client struct {
|
|
mc *minio.Client
|
|
bucket string
|
|
}
|
|
|
|
// New constructs a Client and ensures the configured bucket exists.
|
|
func New(ctx context.Context, cfg config.S3Config) (*Client, error) {
|
|
mc, err := minio.New(cfg.Endpoint, &minio.Options{
|
|
Creds: credentials.NewStaticV4(cfg.AccessKey, cfg.SecretKey, ""),
|
|
Secure: cfg.UseSSL,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("create s3 client: %w", err)
|
|
}
|
|
|
|
exists, err := mc.BucketExists(ctx, cfg.Bucket)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("check bucket: %w", err)
|
|
}
|
|
if !exists {
|
|
if err := mc.MakeBucket(ctx, cfg.Bucket, minio.MakeBucketOptions{}); err != nil {
|
|
return nil, fmt.Errorf("make bucket: %w", err)
|
|
}
|
|
}
|
|
|
|
return &Client{mc: mc, bucket: cfg.Bucket}, nil
|
|
}
|
|
|
|
// Put streams an object of the given size to the bucket under key.
|
|
func (c *Client) Put(ctx context.Context, key string, r io.Reader, size int64, contentType string) error {
|
|
_, err := c.mc.PutObject(ctx, c.bucket, key, r, size, minio.PutObjectOptions{
|
|
ContentType: contentType,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("put object %q: %w", key, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Get returns a reader for the object stored under key. The caller must close it.
|
|
func (c *Client) Get(ctx context.Context, key string) (io.ReadCloser, error) {
|
|
obj, err := c.mc.GetObject(ctx, c.bucket, key, minio.GetObjectOptions{})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get object %q: %w", key, err)
|
|
}
|
|
return obj, nil
|
|
}
|
|
|
|
// Remove deletes the object stored under key.
|
|
func (c *Client) Remove(ctx context.Context, key string) error {
|
|
if err := c.mc.RemoveObject(ctx, c.bucket, key, minio.RemoveObjectOptions{}); err != nil {
|
|
return fmt.Errorf("remove object %q: %w", key, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Ping verifies connectivity to the object store (used by readiness checks).
|
|
func (c *Client) Ping(ctx context.Context) error {
|
|
_, err := c.mc.BucketExists(ctx, c.bucket)
|
|
return err
|
|
}
|