mirror of
https://github.com/dutchcoders/transfer.sh.git
synced 2024-11-27 14:40:18 +01:00
152 lines
3.9 KiB
Go
152 lines
3.9 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"log"
|
|
"time"
|
|
|
|
"storj.io/common/fpath"
|
|
"storj.io/common/storj"
|
|
"storj.io/uplink"
|
|
)
|
|
|
|
// StorjStorage is a storage backed by Storj
|
|
type StorjStorage struct {
|
|
Storage
|
|
project *uplink.Project
|
|
bucket *uplink.Bucket
|
|
purgeDays time.Duration
|
|
logger *log.Logger
|
|
}
|
|
|
|
// NewStorjStorage is the factory for StorjStorage
|
|
func NewStorjStorage(access, bucket string, purgeDays int, logger *log.Logger) (*StorjStorage, error) {
|
|
var instance StorjStorage
|
|
var err error
|
|
|
|
pCtx := context.TODO()
|
|
|
|
ctx := fpath.WithTempData(pCtx, "", true)
|
|
|
|
uplConf := &uplink.Config{
|
|
UserAgent: "transfer-sh",
|
|
}
|
|
|
|
parsedAccess, err := uplink.ParseAccess(access)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
instance.project, err = uplConf.OpenProject(ctx, parsedAccess)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
instance.bucket, err = instance.project.EnsureBucket(ctx, bucket)
|
|
if err != nil {
|
|
//Ignoring the error to return the one that occurred first, but try to clean up.
|
|
_ = instance.project.Close()
|
|
return nil, err
|
|
}
|
|
|
|
instance.purgeDays = time.Duration(purgeDays*24) * time.Hour
|
|
|
|
instance.logger = logger
|
|
|
|
return &instance, nil
|
|
}
|
|
|
|
// Type returns the storage type
|
|
func (s *StorjStorage) Type() string {
|
|
return "storj"
|
|
}
|
|
|
|
// Head retrieves content length of a file from storage
|
|
func (s *StorjStorage) Head(ctx context.Context, token string, filename string) (contentLength uint64, err error) {
|
|
key := storj.JoinPaths(token, filename)
|
|
|
|
obj, err := s.project.StatObject(fpath.WithTempData(ctx, "", true), s.bucket.Name, key)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
contentLength = uint64(obj.System.ContentLength)
|
|
|
|
return
|
|
}
|
|
|
|
// Get retrieves a file from storage
|
|
func (s *StorjStorage) Get(ctx context.Context, token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) {
|
|
key := storj.JoinPaths(token, filename)
|
|
|
|
s.logger.Printf("Getting file %s from Storj Bucket", filename)
|
|
|
|
options := uplink.DownloadOptions{}
|
|
|
|
download, err := s.project.DownloadObject(fpath.WithTempData(ctx, "", true), s.bucket.Name, key, &options)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
contentLength = uint64(download.Info().System.ContentLength)
|
|
|
|
reader = download
|
|
return
|
|
}
|
|
|
|
// Delete removes a file from storage
|
|
func (s *StorjStorage) Delete(ctx context.Context, token string, filename string) (err error) {
|
|
key := storj.JoinPaths(token, filename)
|
|
|
|
s.logger.Printf("Deleting file %s from Storj Bucket", filename)
|
|
|
|
_, err = s.project.DeleteObject(fpath.WithTempData(ctx, "", true), s.bucket.Name, key)
|
|
|
|
return
|
|
}
|
|
|
|
// Purge cleans up the storage
|
|
func (s *StorjStorage) Purge(context.Context, time.Duration) (err error) {
|
|
// NOOP expiration is set at upload time
|
|
return nil
|
|
}
|
|
|
|
// Put saves a file on storage
|
|
func (s *StorjStorage) Put(ctx context.Context, token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) {
|
|
key := storj.JoinPaths(token, filename)
|
|
|
|
s.logger.Printf("Uploading file %s to Storj Bucket", filename)
|
|
|
|
var uploadOptions *uplink.UploadOptions
|
|
if s.purgeDays.Hours() > 0 {
|
|
uploadOptions = &uplink.UploadOptions{Expires: time.Now().Add(s.purgeDays)}
|
|
}
|
|
|
|
writer, err := s.project.UploadObject(fpath.WithTempData(ctx, "", true), s.bucket.Name, key, uploadOptions)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
n, err := io.Copy(writer, reader)
|
|
if err != nil || uint64(n) != contentLength {
|
|
//Ignoring the error to return the one that occurred first, but try to clean up.
|
|
_ = writer.Abort()
|
|
return err
|
|
}
|
|
err = writer.SetCustomMetadata(ctx, uplink.CustomMetadata{"content-type": contentType})
|
|
if err != nil {
|
|
//Ignoring the error to return the one that occurred first, but try to clean up.
|
|
_ = writer.Abort()
|
|
return err
|
|
}
|
|
|
|
err = writer.Commit()
|
|
return err
|
|
}
|
|
|
|
// IsNotExist indicates if a file doesn't exist on storage
|
|
func (s *StorjStorage) IsNotExist(err error) bool {
|
|
return errors.Is(err, uplink.ErrObjectNotFound)
|
|
}
|