62 lines
2.5 KiB
Cheetah
62 lines
2.5 KiB
Cheetah
package service
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"io"
|
|
"time"
|
|
|
|
"git.yuntuo.hk/k2safe/go-omni/logx"
|
|
"git.yuntuo.hk/k2safe/go-omni/storage"
|
|
)
|
|
|
|
// Files borrows provider-neutral capabilities; it never creates or closes clients.
|
|
// Authentication/ownership rules belong to this project, outside storage adapters.
|
|
type Files struct {
|
|
store storage.Store
|
|
uploader *storage.Uploader
|
|
downloads storage.Presigner
|
|
uploadLinks storage.UploadPresigner
|
|
logger *logx.Logger
|
|
ttl time.Duration
|
|
}
|
|
func NewFiles(store storage.Store,uploader *storage.Uploader,downloads storage.Presigner,uploadLinks storage.UploadPresigner,logger *logx.Logger,ttl time.Duration)*Files{
|
|
return &Files{store:store,uploader:uploader,downloads:downloads,uploadLinks:uploadLinks,logger:logger.With("component","files"),ttl:ttl}
|
|
}
|
|
func newFileKey()(string,error){
|
|
var id [16]byte
|
|
if _,err:=rand.Read(id[:]);err!=nil{return "",err}
|
|
return "uploads/"+hex.EncodeToString(id[:]),nil
|
|
}
|
|
func (s *Files) Upload(ctx context.Context,body io.Reader,size int64,contentType string)(storage.Info,error){
|
|
key,err:=newFileKey();if err!=nil{return storage.Info{},err}
|
|
info,err:=s.uploader.Put(ctx,key,body,size,contentType)
|
|
if err!=nil{s.logger.WarnContext(ctx,"file.upload.failed","error",err);return storage.Info{},err}
|
|
s.logger.InfoContext(ctx,"file.uploaded","key",info.Key,"size",info.Size,"contentType",info.ContentType)
|
|
return info,nil
|
|
}
|
|
type FileUploadLink struct {
|
|
Key string `json:"key"`
|
|
Request storage.UploadRequest `json:"request"`
|
|
}
|
|
func (s *Files) UploadLink(ctx context.Context,size int64,contentType string)(FileUploadLink,error){
|
|
key,err:=newFileKey();if err!=nil{return FileUploadLink{},err}
|
|
// Check the same policy before issuing a local or cloud capability.
|
|
// Cloud byte-level checks must happen in a post-upload validation workflow.
|
|
request,err:=s.uploadLinks.PresignPut(ctx,key,size,contentType,s.ttl)
|
|
if err!=nil{return FileUploadLink{},err}
|
|
s.logger.InfoContext(ctx,"file.uploadLink.issued","key",key)
|
|
return FileUploadLink{Key:key,Request:request},nil
|
|
}
|
|
func (s *Files) DownloadLink(ctx context.Context,key string)(string,error){
|
|
if err:=storage.Check(ctx,key);err!=nil{return "",err}
|
|
// Confirm the object exists without buffering its contents.
|
|
object,err:=s.store.Get(ctx,key);if err!=nil{return "",err}
|
|
if err=object.Body.Close();err!=nil{return "",err}
|
|
link,err:=s.downloads.PresignGet(ctx,key,s.ttl)
|
|
if err!=nil{return "",err}
|
|
s.logger.InfoContext(ctx,"file.downloadLink.issued","key",key)
|
|
return link.String(),nil
|
|
}
|