syncthing/lib/s3/s3.go
2024-09-30 14:16:27 -05:00

102 lines
2.2 KiB
Go

// Copyright (C) 2024 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package s3
import (
"io"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
)
type Session struct {
bucket string
s3sess *session.Session
}
type Object = s3.Object
func NewSession(endpoint, region, bucket, accessKeyID, secretKey string) (*Session, error) {
sess, err := session.NewSession(&aws.Config{
Region: aws.String(region),
Endpoint: aws.String(endpoint),
Credentials: credentials.NewStaticCredentials(accessKeyID, secretKey, ""),
})
if err != nil {
return nil, err
}
return &Session{
bucket: bucket,
s3sess: sess,
}, nil
}
func (s *Session) Upload(r io.Reader, key string) error {
uploader := s3manager.NewUploader(s.s3sess)
_, err := uploader.Upload(&s3manager.UploadInput{
Bucket: aws.String(s.bucket),
Key: aws.String(key),
Body: r,
})
return err
}
func (s *Session) List(fn func(*Object) bool) error {
svc := s3.New(s.s3sess)
opts := &s3.ListObjectsV2Input{
Bucket: aws.String(s.bucket),
}
for {
resp, err := svc.ListObjectsV2(opts)
if err != nil {
return err
}
for _, item := range resp.Contents {
if !fn(item) {
return nil
}
}
if resp.NextContinuationToken == nil || *resp.NextContinuationToken == "" {
break
}
opts.ContinuationToken = resp.NextContinuationToken
}
return nil
}
func (s *Session) LatestKey() (string, error) {
var latestKey string
var lastModified time.Time
if err := s.List(func(obj *Object) bool {
if latestKey == "" || obj.LastModified.After(lastModified) {
latestKey = *obj.Key
lastModified = *obj.LastModified
}
return true
}); err != nil {
return "", err
}
return latestKey, nil
}
func (s *Session) Download(w io.WriterAt, key string) error {
downloader := s3manager.NewDownloader(s.s3sess)
_, err := downloader.Download(w, &s3.GetObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(key),
})
return err
}