Skip to content

Commit dcf8812

Browse files
committed
fix: upload to s3 in ovh differ than s3 specs (#451)
**Describe the pull request** This pull request addresses an issue where the upload process to S3 in OVH differs from the standard S3 specifications. The fix involves aligning the upload process with the standard S3 protocol, ensuring compatibility and smooth operation. This fix allow user to upload and access in public-read aby cover uploaded **Checklist** - [x] I have made the modifications or added tests related to my PR - [x] I have run the tests and linters locally and they pass - [x] I have added/updated the documentation for my RP
1 parent 1a2fee3 commit dcf8812

File tree

5 files changed

+114
-30
lines changed

5 files changed

+114
-30
lines changed

config/stud42.example.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ api:
99
bucket: s42-users
1010
region: europe-west1
1111
endpoint: http://localhost:9000
12+
# forcePathStyle is used to force the usage of the path style
13+
# in the endpoint. This is used to use minio as a S3 compatible
14+
# storage in local development, or in production with a provider
15+
# that does not support the virtual host style.
16+
forcePathStyle: true
1217

1318
# Interface relatives configurations
1419
interface: {}

deploy/stacks/apps/configs/stud42/stud42.yaml.tftpl

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ api:
55
bucket: s42-users
66
region: gra
77
endpoint: https://s3.gra.io.cloud.ovh.net
8+
forcePathStyle: false
89

910

1011
# Interface relatives configurations

internal/api/api.resolvers.go

+15-29
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/pkg/s3/client.go

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package s3
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
"time"
7+
8+
"github.com/aws/aws-sdk-go/aws"
9+
"github.com/aws/aws-sdk-go/aws/credentials"
10+
"github.com/aws/aws-sdk-go/aws/session"
11+
"github.com/aws/aws-sdk-go/service/s3"
12+
"github.com/spf13/viper"
13+
)
14+
15+
type S3Client struct {
16+
cfg *viper.Viper
17+
*s3.S3
18+
}
19+
20+
// BucketConfigKey is a type for bucket config keys
21+
// that are used to get bucket config from viper
22+
// config file. This is not the bucket name itself.
23+
type BucketConfigKey string
24+
25+
const (
26+
// UsersBucketConfigKey is a bucket config key for users bucket
27+
// This is not the bucket name itself.
28+
UsersBucketConfigKey BucketConfigKey = "users"
29+
)
30+
31+
// NewS3Client creates a new S3 client with the given bucket config key.
32+
func NewS3Client(bucketConfigKey BucketConfigKey) (*S3Client, error) {
33+
cfg := viper.Sub("api.s3." + string(UsersBucketConfigKey))
34+
s, err := session.NewSession(aws.NewConfig().
35+
WithEndpoint(cfg.GetString("endpoint")).
36+
WithRegion(cfg.GetString("region")).
37+
WithCredentials(credentials.NewCredentials(&credentials.EnvProvider{})),
38+
)
39+
if err != nil {
40+
return nil, err
41+
}
42+
43+
return &S3Client{
44+
cfg: cfg,
45+
S3: s3.New(
46+
s,
47+
aws.NewConfig().
48+
WithS3ForcePathStyle(cfg.GetBool("forcePathStyle")),
49+
),
50+
}, nil
51+
}
52+
53+
// PresignedUploadURL returns a presigned upload URL for the given key.
54+
// The URL will expire after the given expiration duration. The URL will
55+
// be valid for the given content type and content length and will be
56+
// uploaded with the given ACL.
57+
func (c *S3Client) PresignedUploadURL(key, contentType string, contentLength int64, acl string, expiration time.Duration) (string, error) {
58+
request, _ := c.PutObjectRequest(&s3.PutObjectInput{
59+
Bucket: aws.String(c.cfg.GetString("bucket")),
60+
Key: aws.String(key),
61+
ContentType: aws.String(contentType),
62+
ContentLength: aws.Int64(contentLength),
63+
ACL: aws.String(acl),
64+
})
65+
66+
return request.Presign(expiration)
67+
}
68+
69+
// GetBucket returns the bucket name from the config.
70+
func (c *S3Client) GetBucket() string {
71+
return c.cfg.GetString("bucket")
72+
}
73+
74+
// GetBaseURL returns the base URL for the bucket. This is used to
75+
// construct the full URL of the bucket based if the bucket is not
76+
// configured to use path style URLs. If the bucket is configured to
77+
// use path style URLs, the base URL will be the same as the endpoint.
78+
func (c *S3Client) GetBaseURL() string {
79+
s3Endpoint := c.cfg.GetString("endpoint")
80+
if !c.cfg.GetBool("forcePathStyle") {
81+
s3Endpoint = regexp.MustCompile(`://`).ReplaceAllString(
82+
s3Endpoint,
83+
fmt.Sprintf("://%s.", c.cfg.GetString("bucket")),
84+
)
85+
}
86+
87+
return s3Endpoint
88+
}

web/ui/src/pages/settings/profile.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,11 @@ const ProfileSettingPage: NextPage<PageProps> = () => {
109109
fetch(presignedURL, {
110110
method: 'PUT',
111111
body: coverFile,
112-
headers: { 'Content-Type': coverFile.type },
112+
headers: {
113+
'Content-Type': coverFile.type,
114+
'Content-Length': coverFile.size.toString(),
115+
'x-amz-acl': 'public-read',
116+
},
113117
}).then(async (d) => {
114118
if (!d.ok) {
115119
return addNotification({

0 commit comments

Comments
 (0)