Files
drive/apps/backend/internal/upload/service.go

134 lines
2.7 KiB
Go
Raw Normal View History

2025-11-28 22:31:00 +00:00
package upload
import (
"context"
"errors"
"io"
"sync"
"time"
"github.com/get-drexa/drexa/internal/blob"
"github.com/get-drexa/drexa/internal/virtualfs"
"github.com/google/uuid"
)
type Service struct {
vfs *virtualfs.VirtualFS
blobStore blob.Store
pendingUploads sync.Map
}
func NewService(vfs *virtualfs.VirtualFS, blobStore blob.Store) *Service {
return &Service{
vfs: vfs,
blobStore: blobStore,
pendingUploads: sync.Map{},
}
}
type CreateUploadOptions struct {
ParentID string
Name string
}
2025-11-30 17:12:50 +00:00
func (s *Service) CreateUpload(ctx context.Context, accountID uuid.UUID, opts CreateUploadOptions) (*Upload, error) {
parentNode, err := s.vfs.FindNodeByPublicID(ctx, accountID, opts.ParentID)
2025-11-28 22:31:00 +00:00
if err != nil {
if errors.Is(err, virtualfs.ErrNodeNotFound) {
return nil, ErrNotFound
}
return nil, err
}
if parentNode.Kind != virtualfs.NodeKindDirectory {
return nil, ErrParentNotDirectory
}
2025-11-30 17:12:50 +00:00
node, err := s.vfs.CreateFile(ctx, accountID, virtualfs.CreateFileOptions{
2025-11-28 22:31:00 +00:00
ParentID: parentNode.ID,
Name: opts.Name,
})
if err != nil {
if errors.Is(err, virtualfs.ErrNodeConflict) {
return nil, ErrConflict
}
return nil, err
}
uploadURL, err := s.blobStore.GenerateUploadURL(ctx, node.BlobKey, blob.UploadURLOptions{
Duration: 1 * time.Hour,
})
if err != nil {
2025-11-30 01:16:44 +00:00
_ = s.vfs.PermanentlyDeleteNode(ctx, node)
2025-11-28 22:31:00 +00:00
return nil, err
}
upload := &Upload{
ID: node.PublicID,
2025-11-29 17:25:11 +00:00
Status: StatusPending,
2025-11-28 22:31:00 +00:00
TargetNode: node,
UploadURL: uploadURL,
}
s.pendingUploads.Store(upload.ID, upload)
return upload, nil
}
2025-11-30 17:12:50 +00:00
func (s *Service) ReceiveUpload(ctx context.Context, accountID uuid.UUID, uploadID string, reader io.Reader) error {
2025-11-28 22:31:00 +00:00
n, ok := s.pendingUploads.Load(uploadID)
if !ok {
return ErrNotFound
}
upload, ok := n.(*Upload)
if !ok {
return ErrNotFound
}
2025-11-30 17:12:50 +00:00
if upload.TargetNode.AccountID != accountID {
2025-11-28 22:31:00 +00:00
return ErrNotFound
}
err := s.vfs.WriteFile(ctx, upload.TargetNode, virtualfs.FileContentFromReader(reader))
if err != nil {
return err
}
2025-11-29 17:25:11 +00:00
upload.Status = StatusCompleted
2025-11-28 22:31:00 +00:00
return nil
}
2025-11-30 17:12:50 +00:00
func (s *Service) CompleteUpload(ctx context.Context, accountID uuid.UUID, uploadID string) (*Upload, error) {
2025-11-28 22:31:00 +00:00
n, ok := s.pendingUploads.Load(uploadID)
if !ok {
2025-11-29 17:25:11 +00:00
return nil, ErrNotFound
2025-11-28 22:31:00 +00:00
}
upload, ok := n.(*Upload)
if !ok {
2025-11-29 17:25:11 +00:00
return nil, ErrNotFound
2025-11-28 22:31:00 +00:00
}
2025-11-30 17:12:50 +00:00
if upload.TargetNode.AccountID != accountID {
2025-11-29 17:25:11 +00:00
return nil, ErrNotFound
2025-11-28 22:31:00 +00:00
}
2025-11-29 17:25:11 +00:00
if upload.TargetNode.Status == virtualfs.NodeStatusReady && upload.Status == StatusCompleted {
return upload, nil
2025-11-28 22:31:00 +00:00
}
err := s.vfs.WriteFile(ctx, upload.TargetNode, virtualfs.FileContentFromBlobKey(upload.TargetNode.BlobKey))
if err != nil {
2025-11-29 17:25:11 +00:00
return nil, err
2025-11-28 22:31:00 +00:00
}
2025-11-29 17:25:11 +00:00
upload.Status = StatusCompleted
2025-11-28 22:31:00 +00:00
s.pendingUploads.Delete(uploadID)
2025-11-29 17:25:11 +00:00
return upload, nil
2025-11-28 22:31:00 +00:00
}