mirror of
https://github.com/Dvorinka/Productier.git
synced 2026-06-04 12:33:01 +00:00
127 lines
3.1 KiB
Go
127 lines
3.1 KiB
Go
package filestorage
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
"github.com/aws/aws-sdk-go-v2/credentials"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
|
"github.com/aws/smithy-go"
|
|
)
|
|
|
|
type S3Storage struct {
|
|
client *s3.Client
|
|
bucket string
|
|
}
|
|
|
|
func NewS3FromEnv() (*S3Storage, error) {
|
|
bucket := strings.TrimSpace(os.Getenv("S3_BUCKET"))
|
|
accessKey := strings.TrimSpace(os.Getenv("S3_ACCESS_KEY"))
|
|
secretKey := strings.TrimSpace(os.Getenv("S3_SECRET_KEY"))
|
|
if bucket == "" || accessKey == "" || secretKey == "" {
|
|
return nil, errors.New("S3_BUCKET, S3_ACCESS_KEY, and S3_SECRET_KEY are required for FILE_STORAGE_PROVIDER=s3")
|
|
}
|
|
|
|
region := strings.TrimSpace(os.Getenv("S3_REGION"))
|
|
if region == "" {
|
|
region = "us-east-1"
|
|
}
|
|
endpoint := strings.TrimSpace(os.Getenv("S3_ENDPOINT"))
|
|
usePathStyle, _ := strconv.ParseBool(strings.TrimSpace(os.Getenv("S3_USE_PATH_STYLE")))
|
|
|
|
cfg := aws.Config{
|
|
Region: region,
|
|
Credentials: aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")),
|
|
}
|
|
client := s3.NewFromConfig(cfg, func(options *s3.Options) {
|
|
options.UsePathStyle = usePathStyle
|
|
if endpoint != "" {
|
|
options.BaseEndpoint = aws.String(endpoint)
|
|
}
|
|
})
|
|
return &S3Storage{client: client, bucket: bucket}, nil
|
|
}
|
|
|
|
func (s *S3Storage) Provider() string {
|
|
return "s3"
|
|
}
|
|
|
|
func (s *S3Storage) Probe(ctx context.Context) error {
|
|
_, err := s.client.HeadBucket(ctx, &s3.HeadBucketInput{Bucket: aws.String(s.bucket)})
|
|
return err
|
|
}
|
|
|
|
func (s *S3Storage) Put(ctx context.Context, key string, reader io.Reader, contentType string, size int64) error {
|
|
_, err := s.client.PutObject(ctx, &s3.PutObjectInput{
|
|
Bucket: aws.String(s.bucket),
|
|
Key: aws.String(key),
|
|
Body: reader,
|
|
ContentType: aws.String(contentType),
|
|
ContentLength: aws.Int64(size),
|
|
})
|
|
return err
|
|
}
|
|
|
|
func (s *S3Storage) Get(ctx context.Context, key string) (Object, error) {
|
|
response, err := s.client.GetObject(ctx, &s3.GetObjectInput{
|
|
Bucket: aws.String(s.bucket),
|
|
Key: aws.String(key),
|
|
})
|
|
if err != nil {
|
|
if isS3NotFoundError(err) {
|
|
return Object{}, ErrNotFound
|
|
}
|
|
return Object{}, err
|
|
}
|
|
|
|
contentType := ""
|
|
if response.ContentType != nil {
|
|
contentType = *response.ContentType
|
|
}
|
|
size := int64(0)
|
|
if response.ContentLength != nil {
|
|
size = *response.ContentLength
|
|
}
|
|
return Object{
|
|
Body: response.Body,
|
|
ContentType: contentType,
|
|
Size: size,
|
|
}, nil
|
|
}
|
|
|
|
func (s *S3Storage) Delete(ctx context.Context, key string) error {
|
|
_, err := s.client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
|
Bucket: aws.String(s.bucket),
|
|
Key: aws.String(key),
|
|
})
|
|
if err != nil {
|
|
if isS3NotFoundError(err) {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("delete s3 object: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func isS3NotFoundError(err error) bool {
|
|
var noSuchKey *types.NoSuchKey
|
|
if errors.As(err, &noSuchKey) {
|
|
return true
|
|
}
|
|
var apiErr smithy.APIError
|
|
if errors.As(err, &apiErr) {
|
|
switch apiErr.ErrorCode() {
|
|
case "NoSuchKey", "NotFound", "NoSuchBucket":
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|