1620565ae6ac799f2f14acceef4208c1864c3cd9
## Summary When `AWS_ENDPOINT_URL_S3` or `AWS_ENDPOINT_URL` is set — typically because the runtime is pointing at a local MinIO / S3-compatible endpoint — auto-enable path-style addressing on the S3 client. Without this, requests fail because MinIO does not implement virtual-hosted style addressing out of the box. Production deployments leave those env vars unset and continue talking to real AWS S3 with virtual-hosted style — no behaviour change for prod. Both `New()` and `NewS3()` share a `s3ClientOptions` helper that applies the toggle. ## Motivation Spinning up a MinIO-backed acctest environment for Shiny (document-service, invoice-service, accounting-service). Without this change callers would have to sidestep `storage.New` and construct an `aws.Config` by hand just to flip `UsePathStyle`. ## Test plan - [x] New unit test `TestS3ClientOptions_PathStyleTogglesOnCustomEndpoint` covers the three relevant env-var states - [x] `go test ./...` passes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: #99
Storage Module
Shared storage utilities for AWS S3.
Features
- S3 object storage with presigned URL generation
- Two upload strategies: managed uploads (for large files) and direct uploads
- Configurable part size for multipart uploads
- 15-minute presigned URL expiration
Usage
Using the Upload Manager (recommended for large files)
import "gitea.unbound.se/unboundsoftware/storage"
// Create storage with automatic AWS config loading
s3Storage, err := storage.New("my-bucket")
if err != nil {
// handle error
}
// Upload a file and get a presigned URL
url, err := s3Storage.Store("path/to/file.pdf", fileReader, "application/pdf")
Using Direct Upload (for smaller files or custom config)
import (
"github.com/aws/aws-sdk-go-v2/config"
"git.unbound.se/unboundsoftware/storage"
)
// Load custom AWS config
cfg, err := config.LoadDefaultConfig(context.Background())
if err != nil {
// handle error
}
// Create storage with custom config
s3Storage := storage.NewS3(cfg, "my-bucket")
// Upload a file and get a presigned URL
url, err := s3Storage.Store("path/to/file.pdf", fileReader, "application/pdf")
Configuration
The storage module uses AWS SDK v2 and loads configuration from:
- Environment variables (
AWS_REGION,AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY) - Shared configuration files (
~/.aws/config,~/.aws/credentials) - IAM roles (when running on AWS infrastructure)
Description