| 51 | const defaultRegion = "us-east-1" |
| 52 | |
| 53 | func NewBackend(creds *Credentials) (*Backend, error) { |
| 54 | if creds == nil { |
| 55 | return nil, errors.New("credentials cannot be nil") |
| 56 | } |
| 57 | |
| 58 | if err := creds.Validate(); err != nil { |
| 59 | return nil, fmt.Errorf("invalid credentials: %w", err) |
| 60 | } |
| 61 | |
| 62 | // Set a default region if not provided |
| 63 | var region = defaultRegion |
| 64 | if creds.Region != "" { |
| 65 | region = creds.Region |
| 66 | } |
| 67 | |
| 68 | // Bucket might contain the not only the bucket name but also the endpoint |
| 69 | endpoint, bucket, err := extractLocationAndBucket(creds) |
| 70 | if err != nil { |
| 71 | return nil, fmt.Errorf("failed to parse bucket name: %w", err) |
| 72 | } |
| 73 | |
| 74 | // Using AWS config directly instead of using config.LoadDefaultConfig |
| 75 | // to avoid the default credential chain and use only the static credentials |
| 76 | cfg := aws.Config{ |
| 77 | Region: region, |
| 78 | Credentials: credentials.NewStaticCredentialsProvider(creds.AccessKeyID, creds.SecretAccessKey, ""), |
| 79 | } |
| 80 | |
| 81 | // Create S3 client with custom options if needed |
| 82 | var s3Opts []func(*s3.Options) |
| 83 | |
| 84 | // we have a custom endpoint |
| 85 | // in some cases the server-side checksum verification is not supported like in the case of cloudflare r2 |
| 86 | if endpoint != "" { |
| 87 | s3Opts = append(s3Opts, func(o *s3.Options) { |
| 88 | o.BaseEndpoint = aws.String(endpoint) |
| 89 | o.UsePathStyle = true |
| 90 | }) |
| 91 | } |
| 92 | |
| 93 | client := s3.NewFromConfig(cfg, s3Opts...) |
| 94 | |
| 95 | return &Backend{ |
| 96 | client: client, |
| 97 | bucket: bucket, |
| 98 | customEndpoint: endpoint, |
| 99 | }, nil |
| 100 | } |
| 101 | |
| 102 | // For now we are aware that the checksum verification is not supported by cloudflare r2 |
| 103 | // https://developers.cloudflare.com/r2/api/s3/api/ |