Files
honeyDueAPI/internal/dto/responses/upload.go
T
Trey t 7cc5448a7c
Backend CI / Test (push) Has been cancelled
Backend CI / Contract Tests (push) Has been cancelled
Backend CI / Lint (push) Has been cancelled
Backend CI / Secret Scanning (push) Has been cancelled
Backend CI / Build (push) Has been cancelled
fix(uploads): switch from S3 POST policy to presigned PUT
Backblaze B2's S3-compatible endpoint does not implement the S3 POST
Object operation. It returns HTTP 501 to every POST regardless of URL
style — both path-style (https://s3.<region>.backblazeb2.com/<bucket>/)
and virtual-hosted-style (https://<bucket>.s3.<region>.backblazeb2.com/).

Yesterday's BucketLookupDNS fix produced virtual-hosted URLs, which is
correct for AWS but doesn't help here — B2 rejects POST on either form.
Verified with `curl -X POST https://...backblazeb2.com/honeyDueProd/`
returning 501 directly, with no signature involved.

Replace minio-go's PresignedPostPolicy with PresignHeader + http.MethodPut.
The signed URL now points at a single PUT endpoint, with Content-Type
and Content-Length signed via headers — B2/S3/MinIO all accept it. Drop
the min/max content-length range (we sign exactly one length now);
post-upload size verification still happens in VerifyAndClaim via HEAD.

Response shape:
  - URL  (was: signed POST endpoint)  → now: signed PUT URL
  - Fields → renamed to Headers; client sends them as request headers,
    not multipart form parts
  - Method (new): always "PUT", emitted explicitly so clients don't
    have to hardcode

Companion KMP/iOS commits switch the client paths from multipart POST
to single PUT. Existing builds in the field will need to be rebuilt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 15:41:48 -05:00

39 lines
1.6 KiB
Go

package responses
// PresignUploadResponse is what /api/uploads/presign returns to the client.
//
// Flow: the client makes one PUT request to URL with the raw object bytes
// as the body and Headers as the request headers (verbatim — the signature
// binds them). On success, the client passes ID back via upload_ids[] on
// POST /api/task-completions/ or POST /api/documents/ to claim and attach
// the object.
//
// We use PUT (not POST) because Backblaze B2's S3-compatible endpoint does
// not implement the S3 POST Object form upload — it returns HTTP 501 on
// every request style. PUT works against AWS S3, B2, and MinIO uniformly.
type PresignUploadResponse struct {
// ID is the pending_uploads.id the client passes back via upload_ids[].
ID uint `json:"id"`
// URL is the signed PUT URL. Includes all auth as query parameters.
URL string `json:"upload_url"`
// Method is always "PUT" — emitted explicitly so clients don't have to
// hardcode it. Reserved for the rare case we ever offer alternative
// upload mechanisms.
Method string `json:"method"`
// Headers must be sent verbatim on the PUT request. Currently includes
// Content-Type and Content-Length; both are signed, and B2 will reject
// any PUT whose headers don't match.
Headers map[string]string `json:"headers"`
// Key is the object key chosen by the server. Echoed for client logging
// and debugging; the canonical reference is via ID.
Key string `json:"key"`
// ExpiresAt is when the signed URL stops working. Clients should retry
// with a fresh presign rather than relying on long-lived URLs.
ExpiresAt string `json:"expires_at"`
}