mirror of
https://github.com/Dvorinka/Productier.git
synced 2026-06-05 04:53:00 +00:00
first commit
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user