fix(uploads): force virtual-hosted-style URLs for B2 presigned POST

Backblaze B2's S3-compatible endpoint only implements POST Object on
virtual-hosted-style URLs (https://<bucket>.s3.<region>.backblazeb2.com/).
Path-style POST returns HTTP 501 Not Implemented.

minio-go's BucketLookupAuto only flips to virtual-hosted for AWS,
Google, and Aliyun endpoints — for B2 it falls through to path-style,
which is why every PresignedPostPolicy() call has been handing the
mobile clients a URL that B2 then refuses with 501.

Force BucketLookupDNS only when the endpoint is backblazeb2.com so
MinIO dev (no DNS for arbitrary buckets at minio:9000) keeps its
path-style default.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-05-06 13:34:05 -05:00
parent 1347ffadf5
commit 4efc87559a
+14 -2
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"io"
"net/url"
"strings"
"time"
"github.com/minio/minio-go/v7"
@@ -25,11 +26,22 @@ func NewS3Backend(endpoint, keyID, appKey, bucket string, useSSL bool, region st
region = "us-east-1"
}
client, err := minio.New(endpoint, &minio.Options{
opts := &minio.Options{
Creds: credentials.NewStaticV4(keyID, appKey, ""),
Secure: useSSL,
Region: region,
})
}
// B2's S3-compatible endpoint only implements POST Object on
// virtual-hosted-style URLs (https://<bucket>.s3.<region>.backblazeb2.com/).
// Path-style POST returns HTTP 501. minio-go's auto-detection falls back
// to path-style for non-AWS endpoints, which breaks PresignedPostPolicy
// against B2. Force DNS lookup for B2 only — leave MinIO dev (no DNS for
// arbitrary buckets at minio:9000) on the path-style default.
if strings.Contains(endpoint, "backblazeb2.com") {
opts.BucketLookup = minio.BucketLookupDNS
}
client, err := minio.New(endpoint, opts)
if err != nil {
return nil, fmt.Errorf("failed to create S3 client: %w", err)
}