package objects
import (
"context"
"errors"
"fmt"
"net/http"
"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/smithy-go/endpoints"
"github.com/vaughan0/go-ini"
"git.sr.ht/~sircmpwn/core-go/config"
)
var s3CtxKey = &contextKey{"s3"}
type contextKey struct {
name string
}
func Middleware(client *s3.Client) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := Context(r.Context(), client)
r = r.WithContext(ctx)
next.ServeHTTP(w, r)
})
}
}
func Context(ctx context.Context, client *s3.Client) context.Context {
return context.WithValue(ctx, s3CtxKey, client)
}
func ForContext(ctx context.Context) *s3.Client {
raw, ok := ctx.Value(s3CtxKey).(*s3.Client)
if !ok {
panic(fmt.Errorf("Invalid S3 context"))
}
return raw
}
var ErrDisabled = errors.New("object storage is not enabled for this server")
type S3Resolver struct {
conf ini.File
resolver s3.EndpointResolverV2
}
func (r *S3Resolver) ResolveEndpoint(
ctx context.Context,
params s3.EndpointParameters,
) (transport.Endpoint, error) {
upstream, _ := r.conf.Get("objects", "s3-upstream")
endpoint, err := r.resolver.ResolveEndpoint(ctx, params)
if err != nil {
return endpoint, err
}
endpoint.URI.Host = upstream
if params.Bucket != nil {
endpoint.URI.Path = *params.Bucket
}
return endpoint, nil
}
func NewClient(conf ini.File) (*s3.Client, error) {
upstream, _ := conf.Get("objects", "s3-upstream")
accessKey, _ := conf.Get("objects", "s3-access-key")
secretKey, _ := conf.Get("objects", "s3-secret-key")
if upstream == "" || accessKey == "" || secretKey == "" {
return nil, ErrDisabled
}
region, ok := conf.Get("objects", "s3-region")
if !ok {
region = "default"
}
scheme := "https://"
if config.GetBool(conf, "objects", "s3-insecure", false) {
scheme = "http://"
}
creds := credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")
return s3.NewFromConfig(aws.Config{
Region: region,
Credentials: creds,
}, func(opts *s3.Options) {
opts.BaseEndpoint = aws.String(scheme + upstream)
opts.EndpointResolverV2 = &S3Resolver{
conf,
s3.NewDefaultEndpointResolverV2(),
}
}), nil
}
func URL(conf ini.File) string {
upstream, _ := conf.Get("objects", "s3-upstream")
if upstream == "" {
return ""
}
return "https://" + upstream
}
func Enabled(conf ini.File) bool {
upstream, _ := conf.Get("objects", "s3-upstream")
accessKey, _ := conf.Get("objects", "s3-access-key")
secretKey, _ := conf.Get("objects", "s3-secret-key")
return upstream != "" && accessKey != "" && secretKey != ""
}